Trabajo de fin de máster IA


Comparativa de soluciones utilizando series temporales para la predicción de glucosa en diabetes tipo 1 del experimento de 110 pacientes

En este estudio se va a utilizar la metodología del Descubrimiento de Conocimiento en Bases de Datos (KDD) con el objetivo de descubrir patrones, relaciones y tendencias ocultas en los datos, con el fin de tomar decisiones informadas y predecir la glucosa en los 30 y 60 minutos siguientes de los pacientes, y finalmente realizar la comparativa de soluciones con los algoritmos utilizados, las redes neuronales recurrentes (RNN) y redes neuronales recurrentes convolucionales (CRNN).

La metodlogía KDD se compone de cinco fases principales de manera iterativa (se juntan Preprocesamiento y Transformación):

  1. Selección.
  2. Preprocesamiento y Transformación.
  3. Minería de datos.
  4. Evaluación e implantación.

1. Selección¶

Se ha seleccionado para este estudio de 110 pacientes y 4 columnas: "Patient_ID", "Measurement_date", "Measurement_time", "Measurement"

Leer dataframe y mostrar¶

In [1]:
# Librerías
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import sklearn as sklearn
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import r2_score
from sklearn.tree import DecisionTreeRegressor,DecisionTreeClassifier
from sklearn.ensemble import RandomForestRegressor,RandomForestClassifier
from sklearn.model_selection import learning_curve
from sklearn.model_selection import ShuffleSplit
from sklearn.model_selection import cross_val_score
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import KFold
from IPython.display import display, HTML

df = pd.read_csv("Glucose_measurements_sample.csv")
df
Out[1]:
Patient_ID Measurement_date Measurement_time Measurement
0 LIB193263 2020-06-09 19:08:00 99
1 LIB193263 2020-06-09 19:23:00 92
2 LIB193263 2020-06-09 19:38:00 86
3 LIB193263 2020-06-09 19:53:00 85
4 LIB193263 2020-06-09 20:08:00 85
... ... ... ... ...
2999995 LIB193424 2022-01-02 01:05:00 207
2999996 LIB193424 2022-01-02 01:20:00 215
2999997 LIB193424 2022-01-02 01:35:00 218
2999998 LIB193424 2022-01-02 01:50:00 222
2999999 LIB193424 2022-01-02 02:05:00 220

3000000 rows × 4 columns

2. Preprocesamiento y Transformación de datos¶

El preprocesamiento y la transformación de datos son dos fases críticas. Estas fases se dividen en varias tareas:

El preprocesamiento de los datos:

  • Limpieza de los datos: Se eliminan datos incompletos, pero no hay. Lo que se ha hecho es la transformación de los datos.
  • Transformación de los datos: Se transforman los datos en formato adecuado para el análisis.
  • Selección de características: Se selecciona las 4 columnas, los pacientes, fecha, hora de medición y glucosa.

La Transformación de los datos:

  • Extracción de características: Se crean nuevas características a partir de las características existentes. Es decir, se extrae información del conjunto de datos para crear nuevas variables o columnas a partir de las existentes. De esta manera una vez creadas las nuevas características, se pueden utilizar junto con las características originales para entrenar y evaluar el modelo de aprendizaje automático, pudiendo quizá mejorar el rendimiento del modelo.
  • Normalización de datos: Se transforman los datos para que tengan una distribución normal o uniforme, lo que facilita el análisis.

Resumen estadístico de la glucosa (Measurement)¶

Estadística como la media, el mínimo y el máximo, la desviación estándar y los percentiles.

count: es la cantidad de observaciones (mediciones) en los datos. En este caso, hay 3 millones de observaciones.

mean: es la media aritmética de todas las observaciones. En este caso, el valor medio de las mediciones es de 158.95.

std: es la desviación estándar, que mide la cantidad de variación o dispersión que hay en los datos. Una desviación estándar mayor indica que los datos están más dispersos y tienen más variabilidad. En este caso, la desviación estándar es de 65.43.

min: es el valor mínimo en los datos. En este caso, el valor más bajo en las mediciones es 40. Se debe a que el freestlye libre 2 no responde a niveles de glucosa por debajo de 40 mg/dl.

25%: es el primer cuartil, es decir, el valor que es mayor que el 25% de las observaciones. En este caso, el 25% de las mediciones tienen un valor de 110 o menos.

50%: es el segundo cuartil, es decir, el valor que es mayor que el 50% de las observaciones (también conocido como la mediana). En este caso, la mediana de las mediciones es 148.

75%: es el tercer cuartil, es decir, el valor que es mayor que el 75% de las observaciones. En este caso, el 75% de las mediciones tienen un valor de 197 o menos.

max: es el valor máximo en los datos. En este caso, el valor más alto en las mediciones es 500. Se debe a que el freestlye libre 2 no responde a niveles de glucosa por encima de 500 mg/dl.

Los valores de los percentiles (25%, 50% y 75%) proporcionan cómo se distribuyen los datos alrededor de la mediana. En este caso, el hecho de que el tercer cuartil sea 197 significa que el 25% de las mediciones tienen un valor entre 148 y 197.

In [2]:
# Mostrar estadística, como extremos atípicos y grandes desviaciones
df.describe().round(2)
Out[2]:
Measurement
count 3000000.00
mean 158.95
std 65.43
min 40.00
25% 110.00
50% 148.00
75% 197.00
max 500.00

Mostrar los tipos de datos del dataframe¶

Los valores de los datos (string/object, float, int, boolean) son importantes para el manejo de los datos.

Para un mejor manejo de los datos con Pandas, es recomendable que la columna "Patient_ID" contenga valores enteros en lugar de cadenas de texto. Además, para poder aprovechar al máximo las funcionalidades de Pandas, es conveniente que las columnas "Measurement_date" y "Measurement_time" se conviertan a formato de fecha y hora (datetime).

In [3]:
# Los tipos de valores de los datos son float o enteros
df.dtypes
Out[3]:
Patient_ID          object
Measurement_date    object
Measurement_time    object
Measurement          int64
dtype: object

**Limpieza de datos y Transformación de los datos**

La limpieza de los datos es una parte crucial del proceso de obtención, ya que permite adecuar el contenido a nuestras necesidades y hacerlos más fáciles de utilizar. Una forma de mejorar la eficiencia del uso de los datos es renombrar los valores de la columna "Patient_ID" y de las columnas "Measurement_date" y "Measurement_time" para simplificar su uso en pandas y reducir su espacio en pantalla.

Comprobar si hay valores nulos y faltantes¶

In [4]:
# Verificar si hay valores nulos
if df.isnull().any().any():
    print("Hay valores nulos en el dataframe")
else:
    print("No hay valores nulos en el dataframe")

# Verificar si hay valores faltantes
if df.isna().any().any():
    print("Hay valores faltantes en el dataframe")
else:
    print("No hay valores faltantes en el dataframe")
No hay valores nulos en el dataframe
No hay valores faltantes en el dataframe

Muestra los pacientes con su índice único¶

In [5]:
# Obtener una lista de valores únicos de la columna Patient_ID del DataFrame df
unique_patient_ids = df['Patient_ID'].unique()

# Iterar sobre la lista de unique_patient_ids para ver el número de cada paciente único
for i, patient_id in enumerate(unique_patient_ids):
    # Imprimir el número del paciente actual (i) y su ID (patient_id)
    print(f'Número para el paciente {patient_id}: {i}')
Número para el paciente LIB193263: 0
Número para el paciente LIB193264: 1
Número para el paciente LIB193265: 2
Número para el paciente LIB193266: 3
Número para el paciente LIB193267: 4
Número para el paciente LIB193268: 5
Número para el paciente LIB193269: 6
Número para el paciente LIB193272: 7
Número para el paciente LIB193273: 8
Número para el paciente LIB193274: 9
Número para el paciente LIB193276: 10
Número para el paciente LIB193277: 11
Número para el paciente LIB193278: 12
Número para el paciente LIB193279: 13
Número para el paciente LIB193280: 14
Número para el paciente LIB193281: 15
Número para el paciente LIB193282: 16
Número para el paciente LIB193283: 17
Número para el paciente LIB193284: 18
Número para el paciente LIB193302: 19
Número para el paciente LIB193303: 20
Número para el paciente LIB193304: 21
Número para el paciente LIB193307: 22
Número para el paciente LIB193308: 23
Número para el paciente LIB193309: 24
Número para el paciente LIB193310: 25
Número para el paciente LIB193311: 26
Número para el paciente LIB193312: 27
Número para el paciente LIB193313: 28
Número para el paciente LIB193314: 29
Número para el paciente LIB193315: 30
Número para el paciente LIB193317: 31
Número para el paciente LIB193318: 32
Número para el paciente LIB193319: 33
Número para el paciente LIB193320: 34
Número para el paciente LIB193324: 35
Número para el paciente LIB193325: 36
Número para el paciente LIB193326: 37
Número para el paciente LIB193328: 38
Número para el paciente LIB193330: 39
Número para el paciente LIB193332: 40
Número para el paciente LIB193333: 41
Número para el paciente LIB193334: 42
Número para el paciente LIB193335: 43
Número para el paciente LIB193337: 44
Número para el paciente LIB193338: 45
Número para el paciente LIB193340: 46
Número para el paciente LIB193341: 47
Número para el paciente LIB193342: 48
Número para el paciente LIB193343: 49
Número para el paciente LIB193344: 50
Número para el paciente LIB193345: 51
Número para el paciente LIB193346: 52
Número para el paciente LIB193347: 53
Número para el paciente LIB193349: 54
Número para el paciente LIB193350: 55
Número para el paciente LIB193351: 56
Número para el paciente LIB193352: 57
Número para el paciente LIB193353: 58
Número para el paciente LIB193354: 59
Número para el paciente LIB193356: 60
Número para el paciente LIB193357: 61
Número para el paciente LIB193358: 62
Número para el paciente LIB193361: 63
Número para el paciente LIB193363: 64
Número para el paciente LIB193365: 65
Número para el paciente LIB193366: 66
Número para el paciente LIB193367: 67
Número para el paciente LIB193368: 68
Número para el paciente LIB193369: 69
Número para el paciente LIB193370: 70
Número para el paciente LIB193371: 71
Número para el paciente LIB193372: 72
Número para el paciente LIB193375: 73
Número para el paciente LIB193376: 74
Número para el paciente LIB193377: 75
Número para el paciente LIB193378: 76
Número para el paciente LIB193379: 77
Número para el paciente LIB193380: 78
Número para el paciente LIB193381: 79
Número para el paciente LIB193382: 80
Número para el paciente LIB193383: 81
Número para el paciente LIB193384: 82
Número para el paciente LIB193385: 83
Número para el paciente LIB193386: 84
Número para el paciente LIB193387: 85
Número para el paciente LIB193389: 86
Número para el paciente LIB193390: 87
Número para el paciente LIB193391: 88
Número para el paciente LIB193392: 89
Número para el paciente LIB193393: 90
Número para el paciente LIB193395: 91
Número para el paciente LIB193397: 92
Número para el paciente LIB193398: 93
Número para el paciente LIB193399: 94
Número para el paciente LIB193400: 95
Número para el paciente LIB193404: 96
Número para el paciente LIB193406: 97
Número para el paciente LIB193407: 98
Número para el paciente LIB193408: 99
Número para el paciente LIB193410: 100
Número para el paciente LIB193411: 101
Número para el paciente LIB193412: 102
Número para el paciente LIB193414: 103
Número para el paciente LIB193416: 104
Número para el paciente LIB193418: 105
Número para el paciente LIB193419: 106
Número para el paciente LIB193420: 107
Número para el paciente LIB193423: 108
Número para el paciente LIB193424: 109

Convertir los pacientes "Patient_ID" de String (Object) a datos enteros (INT)¶

In [6]:
from sklearn.preprocessing import LabelEncoder

# crear una instancia de LabelEncoder()
le = LabelEncoder()

# convertir las columnas a formato numérico utilizando LabelEncoder()
df['Patient_ID'] = le.fit_transform(df['Patient_ID'])

# Los tipos de valores de los datos son float o enteros
df.dtypes
Out[6]:
Patient_ID           int32
Measurement_date    object
Measurement_time    object
Measurement          int64
dtype: object

Convertir los datos "Measurement_date" y "Measurement_time" de String (Object) a formato de fecha y hora (datetime) para poder manejarlo con pandas¶

In [7]:
df['Measurement_date'] = pd.to_datetime(df['Measurement_date'])
df['Measurement_time'] = pd.to_datetime(df['Measurement_time'], format='%H:%M:%S')

df.dtypes
Out[7]:
Patient_ID                   int32
Measurement_date    datetime64[ns]
Measurement_time    datetime64[ns]
Measurement                  int64
dtype: object

**Extracción de característcas**

La extracción de características consiste en conseguir optimizar el entrenamiento del modelo de predicción de niveles de glucosa en sangre. Se agrega varias columnas al conjunto de datos, como "In_Range", "Hyperglycemia", "Hypoglycemia", "Hour_of_Day" y "Day_of_Week", para analizar cómo estas características afectan la precisión del modelo.

  • "In_Range" indica si una medición está dentro del rango de niveles normales.
  • "Hyperglycemia" indica si una medición está por encima del rango normal.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal
  • "Hour_of_Day" para indicar la hora del día en que se tomó cada medición.
  • "Day_of_Week" para indicar el día de la semana. 0 es lunes y 6 es domingo.

Esta información puede mejorar la precisión del modelo ya que las mediciones que están fuera del rango normal y las mediciones más recientes son más relevantes para predecir los niveles futuros de glucosa, mientras que las características de hora del día y día de la semana pueden ayudar al modelo a capturar patrones diarios y semanales en los niveles de glucosa, lo cual puede mejorar la precisión de las predicciones.

Añadir columna: Tiempo en rango "In_Range"¶

In [8]:
# Crear una nueva columna llamada 'In_Range' que sea True si la medición está dentro del rango y False si no lo está
df['In_Range'] = (df['Measurement'] >= 70) & (df['Measurement'] <= 180)

# Mostrar el resultado
df
Out[8]:
Patient_ID Measurement_date Measurement_time Measurement In_Range
0 0 2020-06-09 1900-01-01 19:08:00 99 True
1 0 2020-06-09 1900-01-01 19:23:00 92 True
2 0 2020-06-09 1900-01-01 19:38:00 86 True
3 0 2020-06-09 1900-01-01 19:53:00 85 True
4 0 2020-06-09 1900-01-01 20:08:00 85 True
... ... ... ... ... ...
2999995 109 2022-01-02 1900-01-01 01:05:00 207 False
2999996 109 2022-01-02 1900-01-01 01:20:00 215 False
2999997 109 2022-01-02 1900-01-01 01:35:00 218 False
2999998 109 2022-01-02 1900-01-01 01:50:00 222 False
2999999 109 2022-01-02 1900-01-01 02:05:00 220 False

3000000 rows × 5 columns

Añadir columna: Fuera de rango "Hypoglycemia" y "Hyperglycemia"¶

In [9]:
# Crear una nueva columna llamada 'Hypoglycemia' que sea True si la medición está por debajo de 70 mg/dl y False si no lo está
df['Hypoglycemia'] = (df['Measurement'] < 70)

# Crear una nueva columna llamada 'Hyperglycemia' que sea True si la medición está por encima de 180 mg/dl y False si no lo está
df['Hyperglycemia'] = (df['Measurement'] > 180)

# Mostrar el resultado
df
Out[9]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia
0 0 2020-06-09 1900-01-01 19:08:00 99 True False False
1 0 2020-06-09 1900-01-01 19:23:00 92 True False False
2 0 2020-06-09 1900-01-01 19:38:00 86 True False False
3 0 2020-06-09 1900-01-01 19:53:00 85 True False False
4 0 2020-06-09 1900-01-01 20:08:00 85 True False False
... ... ... ... ... ... ... ...
2999995 109 2022-01-02 1900-01-01 01:05:00 207 False False True
2999996 109 2022-01-02 1900-01-01 01:20:00 215 False False True
2999997 109 2022-01-02 1900-01-01 01:35:00 218 False False True
2999998 109 2022-01-02 1900-01-01 01:50:00 222 False False True
2999999 109 2022-01-02 1900-01-01 02:05:00 220 False False True

3000000 rows × 7 columns

Añadir columna: Hora del día "Hour_of_Day"¶

In [10]:
# Convertir la columna 'Measurement_time' a formato de hora de Pandas
df['Measurement_time'] = pd.to_datetime(df['Measurement_time'], format='%H:%M:%S').dt.time

# Extraer la hora del día de la columna 'Measurement_time'
df['Hour_of_Day'] = df['Measurement_time'].apply(lambda x: x.hour)

# Mostrar el resultado
df
Out[10]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day
0 0 2020-06-09 19:08:00 99 True False False 19
1 0 2020-06-09 19:23:00 92 True False False 19
2 0 2020-06-09 19:38:00 86 True False False 19
3 0 2020-06-09 19:53:00 85 True False False 19
4 0 2020-06-09 20:08:00 85 True False False 20
... ... ... ... ... ... ... ... ...
2999995 109 2022-01-02 01:05:00 207 False False True 1
2999996 109 2022-01-02 01:20:00 215 False False True 1
2999997 109 2022-01-02 01:35:00 218 False False True 1
2999998 109 2022-01-02 01:50:00 222 False False True 1
2999999 109 2022-01-02 02:05:00 220 False False True 2

3000000 rows × 8 columns

Añadir columna: Día de la semana "Day_of_Week"¶

In [11]:
# Convertir la columna 'Measurement_date' a formato de fecha de Pandas
df['Measurement_date'] = pd.to_datetime(df['Measurement_date'])

# Extraer el día de la semana de la columna 'Measurement_date'
df['Day_of_Week'] = df['Measurement_date'].dt.day_name()

# Mostrar el resultado
df
Out[11]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
0 0 2020-06-09 19:08:00 99 True False False 19 Tuesday
1 0 2020-06-09 19:23:00 92 True False False 19 Tuesday
2 0 2020-06-09 19:38:00 86 True False False 19 Tuesday
3 0 2020-06-09 19:53:00 85 True False False 19 Tuesday
4 0 2020-06-09 20:08:00 85 True False False 20 Tuesday
... ... ... ... ... ... ... ... ... ...
2999995 109 2022-01-02 01:05:00 207 False False True 1 Sunday
2999996 109 2022-01-02 01:20:00 215 False False True 1 Sunday
2999997 109 2022-01-02 01:35:00 218 False False True 1 Sunday
2999998 109 2022-01-02 01:50:00 222 False False True 1 Sunday
2999999 109 2022-01-02 02:05:00 220 False False True 2 Sunday

3000000 rows × 9 columns

Mostrar los tipos de datos del dataframe¶

In [12]:
df.dtypes
Out[12]:
Patient_ID                   int32
Measurement_date    datetime64[ns]
Measurement_time            object
Measurement                  int64
In_Range                      bool
Hypoglycemia                  bool
Hyperglycemia                 bool
Hour_of_Day                  int64
Day_of_Week                 object
dtype: object

Convertir los datos "Measurement_time" de String (Object) a formato hora (datetime) y convertir "Day_of_Week" de String (Object) a formato entero (INT) para poder manejarlo con pandas¶

In [13]:
# Convierte la columna 'Measurement_time' en un objeto datetime de Pandas. En formato '%H:%M:%S' para la hora
df['Measurement_time'] = pd.to_datetime(df['Measurement_time'], format='%H:%M:%S')

# Extraer el día de la semana como un número entero de la columna 'Measurement_date'. 0 es lunes y 6 es domingo
df['Day_of_Week'] = df['Measurement_date'].dt.dayofweek

# Muestra los tipos de datos de cada columna en el DataFrame
df.dtypes
Out[13]:
Patient_ID                   int32
Measurement_date    datetime64[ns]
Measurement_time    datetime64[ns]
Measurement                  int64
In_Range                      bool
Hypoglycemia                  bool
Hyperglycemia                 bool
Hour_of_Day                  int64
Day_of_Week                  int64
dtype: object

Preparación de Series Temporales¶

Las series temporales son una colección de puntos de datos recogidos a intervalos regulares de tiempo. En este contexto, son esenciales para la detección de tendencias, predicciones y patrones. Estos análisis temporales son vitales en numerosos campos, incluyendo las finanzas, la economía, y en este caso específico, en el monitoreo de la glucosa, donde se utilizan para prever niveles de glucosa.

Preparación de Series Temporales:

La preparación se enfoca para la construcción de un modelo basado en series temporales el cual se va a diseñar para predecir los niveles de glucosa en un periodo de 15 y 30 minutos. Para lograr esto, el conjunto de datos en series temporales se va a preparar con intervalos de 15 minutos teniendo observaciones equidistantes en el tiempo entre las fechas de medición.

Posterior a la preparación de Series Temporales:

Posterior a la preparación se va a entrenar el modelo con una longitud de secuencia (seq_length) de 8, que se refiere a la cantidad de pesos temporales que se utilizan como entrada para predecir la salida. Para predecir los próximos 15 y 30 minutos se utiliza un bucle for. Esto permite generar múltiples predicciones en secuencia. Adicionalmente, se utilizado la función pd.date_range() de la biblioteca de Python pandas. Esta función, con un intervalo de 15 minutos y un valor de 'períodos' establecido en 3, crea un rango de fechas que se inicia desde la última fecha conocida (contada como el primer periodo) y genera dos fechas adicionales, cada una en un intervalo de 15 minutos. De esta manera, el total de períodos cubiertos abarca los próximos 15 y 30 minutos desde la última fecha registrada.

Crear un nuevo DataFrame con los datos en Series Temporal¶

Se crea un nuevo DataFrame que organiza los datos en intervalos de tiempo regulares en frecuencias de 15 minutos. Además, se han rellenado los valores faltantes mediante la interpolación lineal, lo que permite analizar los datos como una serie temporal con observaciones equidistantes en el tiempo.

In [14]:
import pandas as pd

# Ordenar el DataFrame por fecha y hora con Datetime
df['Datetime'] = pd.to_datetime(df['Measurement_date']) + pd.to_timedelta(df['Measurement_time'].dt.hour, unit='h') + pd.to_timedelta(df['Measurement_time'].dt.minute, unit='m')
df = df.sort_values(by='Datetime')

# Crear un nuevo DataFrame con los datos en serie temporal
df = df.resample('15T', on='Datetime').agg({
    'Patient_ID': 'first',
    'Measurement_date': 'first',
    'Measurement_time': 'first',
    'Measurement': 'mean',
    'In_Range': 'first',
    'Hypoglycemia': 'first',
    'Hyperglycemia': 'first',
    'Hour_of_Day': 'first',
    'Day_of_Week': 'first'
})

# Rellenar los valores faltantes
df['Measurement'].interpolate(method='linear', inplace=True)

# Redondear los valores en la columna 'Measurement'
df['Measurement'] = df['Measurement'].round()

# Convertir el tipo de datos de cada columna al tipo de datos original
df = df.astype({
    'Patient_ID': 'Int32',
    'Measurement': 'Int64',
    'In_Range': 'bool',
    'Hypoglycemia': 'bool',
    'Hyperglycemia': 'bool',
    'Hour_of_Day': 'Int64',
    'Day_of_Week': 'Int64'
})

Comprobar si hay valores nulos y faltantes¶

In [15]:
# Verificar si hay valores nulos
if df.isnull().any().any():
    print("Hay valores nulos en el dataframe")
else:
    print("No hay valores nulos en el dataframe")

# Verificar si hay valores faltantes
if df.isna().any().any():
    print("Hay valores faltantes en el dataframe")
else:
    print("No hay valores faltantes en el dataframe")
Hay valores nulos en el dataframe
Hay valores faltantes en el dataframe

Muestra los nombres numéricos de los pacientes, son númericos del 0 al 109 siendo un total de 110 pacientes¶

In [16]:
# Obtener una lista de pacientes únicos
pacientes = df["Patient_ID"].unique()
print(pacientes)

print("\n")
# Crear un DataFrame transpuesto con la lista de pacientes
df_pacientes = pd.DataFrame(pacientes).T

# Mostrar el DataFrame transpuesto
print(df_pacientes.to_string(index=False, header=False))

# Encontrar los ID de pacientes faltantes
missing_patients = [patient for patient in range(0, 110) if patient not in pacientes]
print(f"Paciente faltante: {missing_patients}")
<IntegerArray>
[  83, <NA>,   48,   92,   11,   24,    5,   91,   22,    2,
 ...
   10,   44,   13,  103,   64,   57,   38,   33,   65,    7]
Length: 110, dtype: Int32


83 <NA> 48 92 11 24 5 91 22 2 6 40 47 12 43 69 55 60 71 100 75 67 93 59 25 17 74 87 54 21 4 1 0 29 96 84 14 104 86 94 88 3 78 62 99 53 34 68 80 82 50 16 30 39 46 41 77 19 42 28 58 102 95 61 18 81 35 97 98 107 106 26 70 9 49 45 23 73 32 63 27 76 79 15 8 36 56 52 72 31 85 101 108 89 109 90 105 51 20 37 10 44 13 103 64 57 38 33 65 7
Paciente faltante: [66]

Cantidad de todas las filas con valores faltantes¶

In [17]:
# Verificar valores faltantes o nulos en todo el DataFrame
print(df.isnull().sum())

# Verificar valores faltantes o nulos en una columna específica
print(df['Patient_ID'].isnull().sum())
Patient_ID          6609
Measurement_date    6609
Measurement_time    6609
Measurement            0
In_Range               0
Hypoglycemia           0
Hyperglycemia          0
Hour_of_Day         6609
Day_of_Week         6609
dtype: int64
6609

Todos los pacientes menos el paciente 66 (Paciente faltante)¶

In [18]:
# Crear un nuevo DataFrame que excluye filas donde la columna Patient_ID tiene un valor de 66
df_clean = df[df['Patient_ID'] != 66]

# Verificar valores faltantes o nulos en todo el DataFrame
print(df_clean.isnull().sum())

# Verificar valores faltantes o nulos en una columna específica
print(df_clean['Patient_ID'].isnull().sum())
Patient_ID          0
Measurement_date    0
Measurement_time    0
Measurement         0
In_Range            0
Hypoglycemia        0
Hyperglycemia       0
Hour_of_Day         0
Day_of_Week         0
dtype: int64
0

Eliminar valores faltantes, eliminando el paciente "66"¶

In [19]:
# Crear un nuevo DataFrame que excluye filas donde la columna Patient_ID tiene un valor de 66
df = df[df['Patient_ID'] != 66]

Cantidad de todas las filas con valores faltantes¶

In [20]:
# Verificar valores faltantes o nulos en todo el DataFrame
print(df.isnull().sum())

# Verificar valores faltantes o nulos en una columna específica
print(df['Patient_ID'].isnull().sum())
Patient_ID          0
Measurement_date    0
Measurement_time    0
Measurement         0
In_Range            0
Hypoglycemia        0
Hyperglycemia       0
Hour_of_Day         0
Day_of_Week         0
dtype: int64
0

Comprobar si hay valores nulos y faltantes¶

In [21]:
# Verificar si hay valores nulos
if df.isnull().any().any():
    print("Hay valores nulos en el dataframe")
else:
    print("No hay valores nulos en el dataframe")

# Verificar si hay valores faltantes
if df.isna().any().any():
    print("Hay valores faltantes en el dataframe")
else:
    print("No hay valores faltantes en el dataframe")
No hay valores nulos en el dataframe
No hay valores faltantes en el dataframe

3. Minería de datos¶

En esta sección se va a llevar a cabo toda la identificación de patrones y relaciones, análisis exploratorio y estadístico y el uso de algoritmos de aprendizaje automático profundo RNN y CRNN.

Muestra el dataframe preparado¶

Ahora los pacientes son del 0 hasta el 109 y las columnas Measurement_date y Measurement_time son datatime permitiendo manejar sus fechas con pandas

In [22]:
df
Out[22]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-02-21 19:45:00 83 2018-02-21 1900-01-01 19:46:00 121 True False False 19 2
2018-02-21 20:00:00 83 2018-02-21 1900-01-01 20:01:00 124 True False False 20 2
2018-02-21 20:15:00 83 2018-02-21 1900-01-01 20:17:00 131 True False False 20 2
2018-02-21 20:30:00 83 2018-02-21 1900-01-01 20:32:00 128 True False False 20 2
2018-02-21 20:45:00 83 2018-02-21 1900-01-01 20:47:00 129 True False False 20 2
... ... ... ... ... ... ... ... ... ...
2022-03-21 19:45:00 27 2022-03-21 1900-01-01 19:48:00 264 False False True 19 0
2022-03-21 20:00:00 27 2022-03-21 1900-01-01 20:03:00 269 False False True 20 0
2022-03-21 20:15:00 27 2022-03-21 1900-01-01 20:18:00 283 False False True 20 0
2022-03-21 20:30:00 27 2022-03-21 1900-01-01 20:33:00 315 False False True 20 0
2022-03-21 20:45:00 27 2022-03-21 1900-01-01 20:48:00 327 False False True 20 0

136340 rows × 9 columns

In [23]:
df.dtypes
Out[23]:
Patient_ID                   Int32
Measurement_date    datetime64[ns]
Measurement_time    datetime64[ns]
Measurement                  Int64
In_Range                      bool
Hypoglycemia                  bool
Hyperglycemia                 bool
Hour_of_Day                  Int64
Day_of_Week                  Int64
dtype: object

Muestra los nombres numéricos de los pacientes, son númericos del 0 al 109 siendo un total de 110 pacientes¶

In [24]:
pacientes = df["Patient_ID"].unique()
print(pacientes)

print("\n")
df_pacientes = pd.DataFrame(pacientes).T

# Display the transposed DataFrame
print(df_pacientes.to_string(index=False, header=False))

# Find missing patient IDs
missing_patients = [patient for patient in range(0, 110) if patient not in pacientes]
print(f"Paciente faltante: {missing_patients}")
<IntegerArray>
[ 83,  48,  92,  11,  24,   5,  91,  22,   2,   6,
 ...
  10,  44,  13, 103,  64,  57,  38,  33,  65,   7]
Length: 109, dtype: Int32


83 48 92 11 24 5 91 22 2 6 40 47 12 43 69 55 60 71 100 75 67 93 59 25 17 74 87 54 21 4 1 0 29 96 84 14 104 86 94 88 3 78 62 99 53 34 68 80 82 50 16 30 39 46 41 77 19 42 28 58 102 95 61 18 81 35 97 98 107 106 26 70 9 49 45 23 73 32 63 27 76 79 15 8 36 56 52 72 31 85 101 108 89 109 90 105 51 20 37 10 44 13 103 64 57 38 33 65 7
Paciente faltante: [66]

El número de veces que aparece cada uno de los pacientes. De más a menos veces que se han escaneado¶

In [25]:
# Obtener el conteo de valores para la columna 'Patient_ID'
df_vc = df['Patient_ID'].value_counts().reset_index()

# Renombrar las columnas para que sean más descriptivas
df_vc.columns = ['Patient_ID', 'Count']

# Mostrar el resultado
df_vc
Out[25]:
Patient_ID Count
0 24 11987
1 11 11332
2 22 10409
3 83 9198
4 92 8509
... ... ...
104 33 38
105 79 27
106 57 26
107 65 20
108 103 11

109 rows × 2 columns

Mostrar las fechas de inicio y final del estudio de los pacientes¶

In [26]:
df.groupby('Patient_ID')['Measurement_date'].agg(['min', 'max'])
Out[26]:
min max
Patient_ID
0 2020-06-12 2022-03-18
1 2020-06-10 2022-03-06
2 2019-01-27 2022-03-19
3 2020-10-10 2022-03-11
4 2020-06-10 2022-03-17
... ... ...
105 2021-10-29 2022-03-15
106 2021-06-18 2022-03-14
107 2021-06-17 2022-03-11
108 2021-10-25 2022-03-18
109 2021-10-28 2022-01-01

109 rows × 2 columns

Muestra el paciente 1 que es el número 0 (podemos ver que contiene todas sus columnas y filas)¶

In [27]:
df.loc[df["Patient_ID"] == 0]
Out[27]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2020-06-12 00:15:00 0 2020-06-12 1900-01-01 00:16:00 121 True False False 0 4
2020-06-12 00:30:00 0 2020-06-12 1900-01-01 00:31:00 123 True False False 0 4
2020-06-12 00:45:00 0 2020-06-12 1900-01-01 00:46:00 132 True False False 0 4
2020-06-12 10:00:00 0 2020-06-12 1900-01-01 10:01:00 155 True False False 10 4
2020-06-12 10:15:00 0 2020-06-12 1900-01-01 10:16:00 158 True False False 10 4
... ... ... ... ... ... ... ... ... ...
2022-03-18 02:00:00 0 2022-03-18 1900-01-01 02:00:00 170 False False True 2 4
2022-03-18 02:15:00 0 2022-03-18 1900-01-01 02:15:00 170 False False True 2 4
2022-03-18 06:15:00 0 2022-03-18 1900-01-01 06:15:00 154 False False True 6 4
2022-03-18 07:45:00 0 2022-03-18 1900-01-01 07:45:00 153 False False True 7 4
2022-03-18 11:15:00 0 2022-03-18 1900-01-01 11:15:00 173 False False True 11 4

1675 rows × 9 columns

**Dataframe más pequeño para poder manejarlo (5 pacientes)**

Se ha realizado una reducción de datos del DataFrame debido a que la cantidad de información era demasiado grande para ser procesada por la capacidad de cómputo disponible en la computadora utilizada.

Obtener los 5 pacientes con más datos¶

Podemos ver que hay 51435 filas.

In [28]:
# Crear un nuevo DataFrame con los 5 pacientes con más datos
top_5_pacientes = df.groupby('Patient_ID')['Measurement_time'].count().sort_values(ascending=False).head(5).index
df_top_5_pacientes = df[df['Patient_ID'].isin(top_5_pacientes)]
df_top_5_pacientes
Out[28]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-02-21 19:45:00 83 2018-02-21 1900-01-01 19:46:00 121 True False False 19 2
2018-02-21 20:00:00 83 2018-02-21 1900-01-01 20:01:00 124 True False False 20 2
2018-02-21 20:15:00 83 2018-02-21 1900-01-01 20:17:00 131 True False False 20 2
2018-02-21 20:30:00 83 2018-02-21 1900-01-01 20:32:00 128 True False False 20 2
2018-02-21 20:45:00 83 2018-02-21 1900-01-01 20:47:00 129 True False False 20 2
... ... ... ... ... ... ... ... ... ...
2022-03-14 07:00:00 83 2022-03-14 1900-01-01 07:00:00 160 True False False 7 0
2022-03-14 07:15:00 83 2022-03-14 1900-01-01 07:15:00 164 True False False 7 0
2022-03-14 10:00:00 83 2022-03-14 1900-01-01 10:00:00 182 True False False 10 0
2022-03-14 10:45:00 83 2022-03-14 1900-01-01 10:45:00 168 True False False 10 0
2022-03-15 23:45:00 22 2022-03-15 1900-01-01 23:46:00 155 True False False 23 1

51435 rows × 9 columns

Mostrar los 5 pacientes con mayor número de mediciones desde el inicio hasta el final del estudio y su cantidad de mediciones totales junto su media total¶

In [29]:
# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y contar el número de mediciones ('Measurement_time') que cada paciente ha realizado.
# Luego ordenar los resultados de manera descendente ('sort_values(ascending=False)') y seleccionar los primeros 5 pacientes con más mediciones ('head(5)').
top_5_pacientes = df.groupby('Patient_ID')['Measurement_time'].count().sort_values(ascending=False).head(5)

# Crear un nuevo DataFrame 'df_top_5_pacientes_modificado' que contiene los mismos datos que 'top_5_pacientes',
# pero reseteamos el índice para que el ID de paciente y el número de mediciones estén en columnas separadas.
df_top_5_pacientes_modificado = top_5_pacientes.reset_index()

# Renombrar las columnas del DataFrame 'df_top_5_pacientes_modificado'.
df_top_5_pacientes_modificado.columns = ['Patient_ID', 'Num_Mediciones']

# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y obtener la fecha mínima ('min') y máxima ('max') de las mediciones.
# Luego resetear el índice y se guarda en el DataFrame 'fechas_min_max'.
fechas_min_max = df.groupby('Patient_ID')['Measurement_date'].agg(['min', 'max']).reset_index()

# Unir el DataFrame 'df_top_5_pacientes_modificado' y el DataFrame 'fechas_min_max' en una nueva DataFrame 'df_top_5_pacientes_modificado'
# Utilizar el ID de paciente ('Patient_ID') como clave de unión.
df_top_5_pacientes_modificado = df_top_5_pacientes_modificado.merge(fechas_min_max, on='Patient_ID')

# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y calcular la media de la columna 'Measurement' para cada paciente
media_glucosa = df.groupby('Patient_ID')['Measurement'].mean()

# Crear un nuevo DataFrame 'df_media_glucosa' que contiene los mismos datos que 'media_glucosa',
# pero reseteamos el índice para que el ID de paciente y la media de glucosa estén en columnas separadas.
df_media_glucosa = media_glucosa.reset_index()

# Renombrar las columnas del DataFrame 'df_media_glucosa'.
df_media_glucosa.columns = ['Patient_ID', 'Media_Glucosa']

# Unir el DataFrame 'df_top_5_pacientes_modificado' y el DataFrame 'df_media_glucosa' en una nueva DataFrame 'df_top_5_pacientes_modificado'
# Utilizar el ID de paciente ('Patient_ID') como clave de unión.
df_top_5_pacientes_modificado = df_top_5_pacientes_modificado.merge(df_media_glucosa, on='Patient_ID')
df_top_5_pacientes_modificado
Out[29]:
Patient_ID Num_Mediciones min max Media_Glucosa
0 24 11987 2018-07-13 2022-03-12 151.588888
1 11 11332 2018-06-12 2022-02-25 150.851747
2 22 10409 2018-11-06 2022-03-15 157.267653
3 83 9198 2018-02-21 2022-03-14 144.642314
4 92 8509 2018-06-06 2022-01-10 152.568692

Se va a manejar las fechas de verano (junio, julio, agosto) y de invierno (diciembre, enero, febrero y marzo) ya que el calor influye en glucosa del paciente¶

Verano¶

In [30]:
df_verano = df_top_5_pacientes[df_top_5_pacientes['Measurement_date'].dt.month.isin([6,7,8])]
df_verano
Out[30]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-06-06 11:30:00 92 2018-06-06 1900-01-01 11:38:00 372 False False True 11 2
2018-06-06 11:45:00 92 2018-06-06 1900-01-01 11:53:00 365 False False True 11 2
2018-06-06 12:00:00 92 2018-06-06 1900-01-01 12:08:00 345 False False True 12 2
2018-06-06 12:15:00 92 2018-06-06 1900-01-01 12:23:00 327 False False True 12 2
2018-06-06 12:30:00 92 2018-06-06 1900-01-01 12:38:00 305 False False True 12 2
... ... ... ... ... ... ... ... ... ...
2021-08-30 23:30:00 83 2021-08-30 1900-01-01 23:30:00 150 True False False 23 0
2021-08-31 16:45:00 83 2021-08-31 1900-01-01 16:45:00 155 False False True 16 1
2021-08-31 17:45:00 83 2021-08-31 1900-01-01 17:45:00 157 True False False 17 1
2021-08-31 18:00:00 83 2021-08-31 1900-01-01 18:00:00 158 True False False 18 1
2021-08-31 18:45:00 83 2021-08-31 1900-01-01 18:45:00 161 True False False 18 1

13634 rows × 9 columns

Invierno¶

In [31]:
df_inverno = df_top_5_pacientes[df_top_5_pacientes['Measurement_date'].dt.month.isin([12,1,2,3])]
df_inverno
Out[31]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-02-21 19:45:00 83 2018-02-21 1900-01-01 19:46:00 121 True False False 19 2
2018-02-21 20:00:00 83 2018-02-21 1900-01-01 20:01:00 124 True False False 20 2
2018-02-21 20:15:00 83 2018-02-21 1900-01-01 20:17:00 131 True False False 20 2
2018-02-21 20:30:00 83 2018-02-21 1900-01-01 20:32:00 128 True False False 20 2
2018-02-21 20:45:00 83 2018-02-21 1900-01-01 20:47:00 129 True False False 20 2
... ... ... ... ... ... ... ... ... ...
2022-03-14 07:00:00 83 2022-03-14 1900-01-01 07:00:00 160 True False False 7 0
2022-03-14 07:15:00 83 2022-03-14 1900-01-01 07:15:00 164 True False False 7 0
2022-03-14 10:00:00 83 2022-03-14 1900-01-01 10:00:00 182 True False False 10 0
2022-03-14 10:45:00 83 2022-03-14 1900-01-01 10:45:00 168 True False False 10 0
2022-03-15 23:45:00 22 2022-03-15 1900-01-01 23:46:00 155 True False False 23 1

17535 rows × 9 columns

La media de la glucosa según los distintos dataframes¶

In [32]:
# Calcular las medias para cada DataFrame
media_df = df['Measurement'].mean()
media_df_top_5_pacientes = df_top_5_pacientes['Measurement'].mean()
media_df_verano = df_verano['Measurement'].mean()
media_df_inverno = df_inverno['Measurement'].mean()

# Crear un nuevo DataFrame con los valores de las medias
df_medias = pd.DataFrame({'DataFrame': ['df (completo)', 'df_top_5_pacientes', 'df_verano (top_5_pacientes)', 'df_inverno (top_5_pacientes)'],
                          'Media': [media_df, media_df_top_5_pacientes, media_df_verano, media_df_inverno]})
# Mostrar el resultado
df_medias
Out[32]:
DataFrame Media
0 df (completo) 154.892599
1 df_top_5_pacientes 151.495557
2 df_verano (top_5_pacientes) 144.856829
3 df_inverno (top_5_pacientes) 154.311263

Valores que están en rango¶

In [33]:
# Seleccionar las filas con Measurement (medición de glucosa) entre 70 mg/dl y 180 mg/dl (Tiempo en rango ideal según TIR)
df_top_5_pacientes[(df_top_5_pacientes['Measurement'] >= 70) & (df_top_5_pacientes['Measurement'] <= 180)]
df_top_5_pacientes
Out[33]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-02-21 19:45:00 83 2018-02-21 1900-01-01 19:46:00 121 True False False 19 2
2018-02-21 20:00:00 83 2018-02-21 1900-01-01 20:01:00 124 True False False 20 2
2018-02-21 20:15:00 83 2018-02-21 1900-01-01 20:17:00 131 True False False 20 2
2018-02-21 20:30:00 83 2018-02-21 1900-01-01 20:32:00 128 True False False 20 2
2018-02-21 20:45:00 83 2018-02-21 1900-01-01 20:47:00 129 True False False 20 2
... ... ... ... ... ... ... ... ... ...
2022-03-14 07:00:00 83 2022-03-14 1900-01-01 07:00:00 160 True False False 7 0
2022-03-14 07:15:00 83 2022-03-14 1900-01-01 07:15:00 164 True False False 7 0
2022-03-14 10:00:00 83 2022-03-14 1900-01-01 10:00:00 182 True False False 10 0
2022-03-14 10:45:00 83 2022-03-14 1900-01-01 10:45:00 168 True False False 10 0
2022-03-15 23:45:00 22 2022-03-15 1900-01-01 23:46:00 155 True False False 23 1

51435 rows × 9 columns

In [34]:
# Filtrar solo las mediciones del día 9 de junio de 2020
df_day = df_top_5_pacientes[(df_top_5_pacientes['Measurement_date'] == '2020-06-09')]

# Contar cuántas mediciones están dentro del rango y calcular el porcentaje
num_in_range = len(df_day[(df_day['Measurement'] >= 70) & (df_day['Measurement'] <= 180)])
percent_in_range = num_in_range / len(df_day) * 100

print('Porcentaje de mediciones dentro del rango: {:.2f}%'.format(percent_in_range))
Porcentaje de mediciones dentro del rango: 100.00%

**Análisis del Paciente 22**

  • Measurement_date: Extraer el día de la semana (ya que no es lo mismo de lunes a viernes que de sabado a domingo o principios de mes que a finales de mes)
  • Measuremenet_time: Muestra el momento del día ya que no es lo mismo que sea por la noche a que sea por el día, tampoco es lo mismo que sea por la mañana a que sea por la tarde, o antes o después del desayuno, comida o cena. “factor del alba, depende de la hora del dia la sensibilidad a la insulina es distinta".
  • Measurement: Muestra los niveles de glucosa en cada medición. Se utiliza para calcular métricas como el tiempo en rango, la hipoglucemia y la hiperglucemia, que brindan información sobre el control glucémico.
  • Hypoglycemia (Hipoglucemia): Es de tipo booleano y se extrae comparando cada medición de glucosa con un umbral predefinido, por ejemplo, 70 mg/dL. Si el valor de la medición es menor que este umbral, se establece la columna "Hypoglycemia" como Verdadero (True); de lo contrario, se establece como Falso (False). Esto indica si la medición muestra niveles de glucosa por debajo del umbral de hipoglucemia.
  • Hyperglycemia (Hiperglucemia): Es de tipo booleano y se extrae comparando cada medición de glucosa con otro umbral predefinido, por ejemplo, 180 mg/dL. Si el valor de la medición es mayor que este umbral, se establece la columna "Hyperglycemia" como Verdadero (True); de lo contrario, se establece como Falso (False). Esto indica si la medición muestra niveles de glucosa por encima del umbral de hiperglucemia.
  • Hour_of_Day: Se deriva del campo "Measurement_time" y representa la hora en que se tomó cada medición. Ayuda a identificar patrones diarios en los niveles de glucosa.
  • Day_of_Week: Se deriva de la columna "Measurement_date" y muestra el día de la semana en que se realizó cada medición. Permite capturar patrones semanales en los niveles de glucosa.

Todos los datos del paciente 22¶

In [35]:
# Filtrar los datos para el paciente 22
df_paciente_22 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 22]

# Imprimir los resultados
df_paciente_22
Out[35]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-11-06 06:45:00 22 2018-11-06 1900-01-01 06:45:00 134 True False False 6 1
2018-11-06 07:00:00 22 2018-11-06 1900-01-01 07:00:00 134 True False False 7 1
2018-11-06 07:15:00 22 2018-11-06 1900-01-01 07:15:00 139 True False False 7 1
2018-11-06 07:30:00 22 2018-11-06 1900-01-01 07:30:00 145 True False False 7 1
2018-11-06 07:45:00 22 2018-11-06 1900-01-01 07:45:00 147 True False False 7 1
... ... ... ... ... ... ... ... ... ...
2022-03-08 11:30:00 22 2022-03-08 1900-01-01 11:30:00 179 True False False 11 1
2022-03-08 12:30:00 22 2022-03-08 1900-01-01 12:30:00 173 True False False 12 1
2022-03-08 12:45:00 22 2022-03-08 1900-01-01 12:45:00 171 True False False 12 1
2022-03-08 13:15:00 22 2022-03-08 1900-01-01 13:15:00 165 True False False 13 1
2022-03-15 23:45:00 22 2022-03-15 1900-01-01 23:46:00 155 True False False 23 1

10409 rows × 9 columns

Extraer la media de la glucosa entre semana a diferencia con el fin de semana¶

Se va a calcular del mismo año y mes la glucosa media según si es entre semana o fin de semana.

El paciente con mayor cantidad de mediciones en un mes completo¶

Muestra el paciente con la mayor cantidad de mediciones y con su total de mediciones, además de la fecha correspondiente.

El paciente ID 22 en un mes tiene 1565 mediciones 1/2019.

In [36]:
# Agrupar los datos por Patient_ID y mes de Measurement_date
grouped_df = df_top_5_pacientes.groupby(['Patient_ID', df_top_5_pacientes['Measurement_date'].dt.to_period('M')])

# Encontrar el Patient_ID y la fecha (mes y año) para el paciente con la mayor cantidad de mediciones
result = grouped_df.size().idxmax()

# Calcular el total de mediciones para el paciente con la mayor cantidad de mediciones
total_measurements = grouped_df.size().max()

# Extraer el Patient_ID, año y mes del resultado
patient_id, date = result
year, month = date.year, date.month

# Imprimir los resultados
print(f'El paciente con ID {patient_id} tiene el mayor número de mediciones en un mes con un total de {total_measurements} mediciones en {month}/{year}.')
El paciente con ID 22 tiene el mayor número de mediciones en un mes con un total de 1565 mediciones en 1/2019.

Mostrar el paciente 22 (2019)¶

In [37]:
# Filtrar los datos para el paciente 83 del año 2021
df_paciente_22_2019 = df_top_5_pacientes[(df_top_5_pacientes['Patient_ID'] == 22) & (df_top_5_pacientes['Measurement_date'].dt.year == 2019)]

# Imprimir los resultados
df_paciente_22_2019
Out[37]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2019-01-01 00:00:00 22 2019-01-01 1900-01-01 00:00:00 194 True False False 0 1
2019-01-01 00:15:00 22 2019-01-01 1900-01-01 00:15:00 205 True False False 0 1
2019-01-01 00:30:00 22 2019-01-01 1900-01-01 00:30:00 216 True False False 0 1
2019-01-01 00:45:00 22 2019-01-01 1900-01-01 00:45:00 217 True False False 0 1
2019-01-01 01:00:00 22 2019-01-01 1900-01-01 01:00:00 225 True False False 1 1
... ... ... ... ... ... ... ... ... ...
2019-12-31 15:30:00 22 2019-12-31 1900-01-01 15:36:00 145 True False False 15 1
2019-12-31 15:45:00 22 2019-12-31 1900-01-01 15:51:00 140 True False False 15 1
2019-12-31 16:00:00 22 2019-12-31 1900-01-01 16:06:00 133 True False False 16 1
2019-12-31 16:15:00 22 2019-12-31 1900-01-01 16:21:00 134 True False False 16 1
2019-12-31 16:30:00 22 2019-12-31 1900-01-01 16:36:00 133 True False False 16 1

6099 rows × 9 columns

Mostrar el mes 1 completo del paciente 22 (1/2019)¶

In [38]:
# pd.set_option('display.max_rows', None)
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_top_5_pacientes[(df_top_5_pacientes['Patient_ID'] == 22) & (df_top_5_pacientes['Measurement_date'].dt.year == 2019) & (df_top_5_pacientes['Measurement_date'].dt.month == 1)]

# Imprimir los resultados
df_paciente_22_1_2019
Out[38]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2019-01-01 00:00:00 22 2019-01-01 1900-01-01 00:00:00 194 True False False 0 1
2019-01-01 00:15:00 22 2019-01-01 1900-01-01 00:15:00 205 True False False 0 1
2019-01-01 00:30:00 22 2019-01-01 1900-01-01 00:30:00 216 True False False 0 1
2019-01-01 00:45:00 22 2019-01-01 1900-01-01 00:45:00 217 True False False 0 1
2019-01-01 01:00:00 22 2019-01-01 1900-01-01 01:00:00 225 True False False 1 1
... ... ... ... ... ... ... ... ... ...
2019-01-31 22:45:00 22 2019-01-31 1900-01-01 22:52:00 146 True False False 22 3
2019-01-31 23:00:00 22 2019-01-31 1900-01-01 23:07:00 145 True False False 23 3
2019-01-31 23:15:00 22 2019-01-31 1900-01-01 23:22:00 144 True False False 23 3
2019-01-31 23:30:00 22 2019-01-31 1900-01-01 23:37:00 141 True False False 23 3
2019-01-31 23:45:00 22 2019-01-31 1900-01-01 23:52:00 145 True False False 23 3

1565 rows × 9 columns

Verificar que todos los días el paciente 22 tiene mediciones y no tiene valores nulos (1/2019)¶

In [39]:
# Crear una copia del dataframe df_top_5_pacientes
df_paciente_22_1_2019_copia = df_paciente_22_1_2019.copy()

# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Verificar que todos los días del mes tienen mediciones
all_days_have_measurements = df_paciente_22_1_2019['Measurement_date'].nunique() == df_paciente_22_1_2019['Measurement_date'].iloc[0].days_in_month

# Verificar que no hay valores nulos en la columna Measurement
no_null_values = df_paciente_22_1_2019_copia['Measurement'].notnull().all()

# Imprimir los resultados
if all_days_have_measurements:
    print('Todos los días del mes tienen mediciones.')
else:
    print('No todos los días del mes tienen mediciones.')

if no_null_values:
    print('No hay valores nulos en la columna Measurement.')
else:
    print('Hay valores nulos en la columna Measurement.')
No todos los días del mes tienen mediciones.
No hay valores nulos en la columna Measurement.

Gráfico de barras y de líneas que muestra el número de mediciones para cada día del mes del paciente 22 (1/2019)¶

In [40]:
# Gráfico de barras
# Agrupar por día y contar el número de mediciones
conteo_mediciones = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.day)['Measurement'].size().reset_index()
conteo_mediciones.columns = ['Día', 'Num_Mediciones']

# Crear una figura y un eje
fig, ax = plt.subplots()

# Crear el gráfico de barras
ax.bar(conteo_mediciones['Día'], conteo_mediciones['Num_Mediciones'])

# Establecer las etiquetas de los ejes
ax.set_xlabel('Día')
ax.set_ylabel('Número de mediciones')

# Mostrar el gráfico de barras
plt.show()

# Gráfico de líneas
# Agrupar por día y contar el número de mediciones
conteo_mediciones = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.day)['Measurement'].size().reset_index()
conteo_mediciones.columns = ['Día', 'Num_Mediciones']

# Crear una figura y un eje
fig, ax = plt.subplots()

# Crear el gráfico de líneas
ax.plot(conteo_mediciones['Día'], conteo_mediciones['Num_Mediciones'])

# Establecer las etiquetas de los ejes
ax.set_xlabel('Día')
ax.set_ylabel('Número de mediciones')

# Mostrar el gráfico de líneas
plt.show()

Verificar que no hay valores anómalos en la glucosa (Measurement) del paciente 22 (2019)¶

El freestlye libre 2 no puede capturar valores de glucosa inferiores a 40 o superiores a 500

In [41]:
# Verificar si hay valores anómalos en la columna Measurement
anomalous_values = df_paciente_22_2019[(df_paciente_22_2019['Measurement'] < 40) | (df_paciente_22_2019['Measurement'] > 500)]

# Imprimir los resultados
if anomalous_values.empty:
    print('No hay valores anómalos en la columna Measurement.')
else:
    print('Hay valores anómalos en la columna Measurement:')
    print(anomalous_values)
No hay valores anómalos en la columna Measurement.

Extrae los días y la media de la glucosa entre semana (lunes a viernes) del paciente 22 (1/2019)¶

In [42]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Filtrar solo los días de entre semana (lunes a viernes)
df_top_5_pacientes_dias = df_paciente_22_1_2019[df_paciente_22_1_2019['Measurement_date'].dt.dayofweek < 5]

# Calcular la media de la columna Measurement para los días de entre semana
mean_measurement = df_top_5_pacientes_dias['Measurement'].mean()

# Imprimir el resultado
print(f'La media de la glucosa para los días de entre semana es: {mean_measurement:.2f}')
df_top_5_pacientes_dias
La media de la glucosa para los días de entre semana es: 155.27
Out[42]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2019-01-01 00:00:00 22 2019-01-01 1900-01-01 00:00:00 194 True False False 0 1
2019-01-01 00:15:00 22 2019-01-01 1900-01-01 00:15:00 205 True False False 0 1
2019-01-01 00:30:00 22 2019-01-01 1900-01-01 00:30:00 216 True False False 0 1
2019-01-01 00:45:00 22 2019-01-01 1900-01-01 00:45:00 217 True False False 0 1
2019-01-01 01:00:00 22 2019-01-01 1900-01-01 01:00:00 225 True False False 1 1
... ... ... ... ... ... ... ... ... ...
2019-01-31 22:45:00 22 2019-01-31 1900-01-01 22:52:00 146 True False False 22 3
2019-01-31 23:00:00 22 2019-01-31 1900-01-01 23:07:00 145 True False False 23 3
2019-01-31 23:15:00 22 2019-01-31 1900-01-01 23:22:00 144 True False False 23 3
2019-01-31 23:30:00 22 2019-01-31 1900-01-01 23:37:00 141 True False False 23 3
2019-01-31 23:45:00 22 2019-01-31 1900-01-01 23:52:00 145 True False False 23 3

1099 rows × 9 columns

Gráfico de barras y de líneas con los días y la media de la glucosa entre semana (lunes a viernes) del paciente 22 (1/2019)¶

In [43]:
import matplotlib.pyplot as plt
# Gráfico de barras
# Agrupar por día y calcular la media de las mediciones
mediciones_dias = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.day)['Measurement'].mean().reset_index()
mediciones_dias.columns = ['Día', 'Media_Mediciones']

# Crear una figura y un eje
fig, ax = plt.subplots()

# Crear el gráfico de barras
ax.bar(mediciones_dias['Día'], mediciones_dias['Media_Mediciones'])

# Establecer las etiquetas de los ejes
ax.set_xlabel('Día')
ax.set_ylabel('Media de mediciones')

# Mostrar el gráfico de barras
plt.show()

# Gráfico de líneas
# Crear una figura y un eje
fig, ax = plt.subplots()

# Crear el gráfico de líneas
ax.plot(mediciones_dias['Día'], mediciones_dias['Media_Mediciones'])

# Establecer las etiquetas de los ejes
ax.set_xlabel('Día')
ax.set_ylabel('Media de mediciones')

# Mostrar el gráfico de líneas
plt.show()

Extrae los días y la media de la glucosa del fin de semana (sabado y domingo) del paciente 22 (1/2019)¶

In [44]:
# Filtrar los datos para el paciente 83 en el mes 10 del año 2021
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Filtrar solo los fines de semana (sábado y domingo)
df_top_5_pacientes_dias = df_paciente_22_1_2019[df_paciente_22_1_2019['Measurement_date'].dt.dayofweek >= 5]

# Calcular la media de la columna Measurement para los fines de semana
mean_measurement = df_top_5_pacientes_dias['Measurement'].mean()

# Imprimir el resultado
print(f'La media de la glucosa para los fin de semana es: {mean_measurement:.2f}')
df_top_5_pacientes_dias
La media de la glucosa para los fin de semana es: 165.48
Out[44]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2019-01-05 00:00:00 22 2019-01-05 1900-01-01 00:00:00 216 False False True 0 5
2019-01-05 00:15:00 22 2019-01-05 1900-01-01 00:15:00 219 False False True 0 5
2019-01-05 00:30:00 22 2019-01-05 1900-01-01 00:30:00 223 False False True 0 5
2019-01-05 00:45:00 22 2019-01-05 1900-01-01 00:45:00 225 False False True 0 5
2019-01-05 01:00:00 22 2019-01-05 1900-01-01 01:00:00 190 False False True 1 5
... ... ... ... ... ... ... ... ... ...
2019-01-27 22:45:00 22 2019-01-27 1900-01-01 22:56:00 251 False False True 22 6
2019-01-27 23:00:00 22 2019-01-27 1900-01-01 23:11:00 219 False False True 23 6
2019-01-27 23:15:00 22 2019-01-27 1900-01-01 23:26:00 207 False False True 23 6
2019-01-27 23:30:00 22 2019-01-27 1900-01-01 23:41:00 194 False False True 23 6
2019-01-27 23:45:00 22 2019-01-27 1900-01-01 23:56:00 176 False False True 23 6

466 rows × 9 columns

Gráfico de barras y de líneas con los días y la media de la glucosa del fin de semana (sabado y domingo) del paciente 22 (1/2019)¶

In [45]:
import matplotlib.pyplot as plt

# Crear un nuevo DataFrame con los valores medios de Measurement para cada día del fin de semana
mediciones_dias = df_paciente_22_1_2019[df_paciente_22_1_2019['Measurement_date'].dt.dayofweek >= 5]
mediciones_dias = mediciones_dias.groupby(mediciones_dias['Measurement_date'].dt.date)['Measurement'].mean().reset_index()

# Crear un gráfico de barras con rotación x-axis labels
plt.bar(mediciones_dias['Measurement_date'], mediciones_dias['Measurement'])
plt.title('Average Glucose Measurements on Weekends in October 2021')
plt.xlabel('Date')
plt.ylabel('Average Glucose Measurement')
plt.xticks(rotation=45) # Rotar las etiquetas del eje x en 45 grados
plt.show()

# Crear un gráfico de líneas
plt.plot(mediciones_dias['Measurement_date'], mediciones_dias['Measurement'])
plt.title('Average Glucose Measurements on Weekends in October 2021')
plt.xlabel('Date')
plt.ylabel('Average Glucose Measurement')
plt.xticks(rotation=45) # Rotar las etiquetas del eje x en 45 grados
plt.show()

Extraer el día con mayor cantidad de mediciones del paciente 22 (1/2019)¶

In [46]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Agrupar los datos por día y contar el número de mediciones por día
daily_counts = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.date)['Measurement_time'].count()

# Encontrar el día con la mayor cantidad de mediciones
max_day = daily_counts.idxmax()

# Obtener la cantidad de mediciones para ese día
max_count = daily_counts.loc[max_day]

# Imprimir el resultado
print(f'El día con la mayor cantidad de mediciones es: {max_day} con {max_count} mediciones.')
El día con la mayor cantidad de mediciones es: 2019-01-05 con 96 mediciones.

Extraer la media de cantidad de mediciones del mes y la cantidad máxima de mediciones de cada día del paciente 22 (1/2019)¶

In [47]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Agrupar los datos por día y contar el número de mediciones por día
daily_counts = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.date)['Measurement_time'].count()

# Encontrar el día con la mayor cantidad de mediciones
max_day = daily_counts.idxmax()

# Obtener la cantidad de mediciones para ese día
max_count = daily_counts.loc[max_day]
mean_count = daily_counts.mean()

print(f'La media de cantidad de mediciones por día es: {mean_count:.2f}')

# Crear un nuevo DataFrame con el conteo diario de mediciones
df_conteo_diario = pd.DataFrame({'Día': daily_counts.index, 'Cantidad de mediciones': daily_counts.values})

# Mostrar el resultado
print('\nConteo diario de mediciones:')
display(df_conteo_diario)
La media de cantidad de mediciones por día es: 53.97

Conteo diario de mediciones:
Día Cantidad de mediciones
0 2019-01-01 60
1 2019-01-02 75
2 2019-01-03 85
3 2019-01-04 93
4 2019-01-05 96
5 2019-01-06 76
6 2019-01-07 82
7 2019-01-08 82
8 2019-01-09 80
9 2019-01-10 24
10 2019-01-11 62
11 2019-01-12 50
12 2019-01-13 61
13 2019-01-15 61
14 2019-01-16 50
15 2019-01-17 16
16 2019-01-19 40
17 2019-01-20 66
18 2019-01-21 23
19 2019-01-22 7
20 2019-01-23 71
21 2019-01-24 3
22 2019-01-25 52
23 2019-01-26 12
24 2019-01-27 65
25 2019-01-28 61
26 2019-01-29 36
27 2019-01-30 10
28 2019-01-31 66

Del día con mayor cantidad de mediciones de glucosa tomadas, se muestra el tiempo de cada una de las mediciones tomadas y el tiempo transcurrido de una medición a la siguiente medición. Además se muestra la medición de la glucosa en relación con las mediciones tomadas del paciente 22 (5/1/2019)¶

In [48]:
# Filtrar los datos para el paciente 22 en el día 5 del mes 1 del año 2019
df_paciente_22_5_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1) & (df_paciente_22_1_2019['Measurement_date'].dt.day == 5)]

# Calcular las mediciones tomadas (Measurement_time) para ese día
day_measurements = df_paciente_22_5_1_2019['Measurement_time'].dt.time

# Calcular el tiempo transcurrido entre cada medición
time_diff = df_paciente_22_5_1_2019['Measurement_time'].diff()

# Crear un nuevo DataFrame con las mediciones tomadas y el tiempo transcurrido entre cada medición como columnas
result = pd.DataFrame({'Measurement_time': day_measurements, 'Time_diff': time_diff})

result['Measurement'] = df_paciente_22_5_1_2019['Measurement']

# Imprimir el resultado
print(f'Las mediciones tomadas el día 5 del mes 1 del año 2019 y el tiempo transcurrido entre cada medición son:')
result
Las mediciones tomadas el día 5 del mes 1 del año 2019 y el tiempo transcurrido entre cada medición son:
Out[48]:
Measurement_time Time_diff Measurement
Datetime
2019-01-05 00:00:00 00:00:00 NaT 216
2019-01-05 00:15:00 00:15:00 0 days 00:15:00 219
2019-01-05 00:30:00 00:30:00 0 days 00:15:00 223
2019-01-05 00:45:00 00:45:00 0 days 00:15:00 225
2019-01-05 01:00:00 01:00:00 0 days 00:15:00 190
... ... ... ...
2019-01-05 22:45:00 22:46:00 0 days 00:15:00 224
2019-01-05 23:00:00 23:01:00 0 days 00:15:00 222
2019-01-05 23:15:00 23:16:00 0 days 00:15:00 193
2019-01-05 23:30:00 23:31:00 0 days 00:15:00 190
2019-01-05 23:45:00 23:46:00 0 days 00:15:00 189

96 rows × 3 columns

Gráfica lineal que muestra la tendencia de la glucosa durante el día y tiene un sombreado que representa el tiempo en rango entre 70 mg/dl y 180 mg/dl en la fecha 5/1/2019 del paciente 22¶

Muestra desde inicio del día hasta final del día, y mostrando en horizontal (X) las horas del día separadas en 2 horas y mostrando en Vertical (Y) desde 40 hasta 500 que es lo que captura el freestlye libre 2. También en la gráfica lineal representar en puntos las mediciones de glucosa (Measurement_time) tomadas, y abajo se muestra una tabla con los valores de la glucosa representados en puntos en el gráfico.

In [49]:
import matplotlib.pyplot as plt
from matplotlib.table import Table

# Crear una figura y un eje para la gráfica
fig1, ax1 = plt.subplots(figsize=(15, 4))

# Convertir la columna 'Measurement_time' a formato datetime y extraer solo la hora
df_paciente_22_5_1_2019 = df_paciente_22_5_1_2019.copy() # quita advertencia
df_paciente_22_5_1_2019['hour'] = pd.to_datetime(df_paciente_22_5_1_2019['Measurement_time']).dt.hour


# Crear la gráfica lineal
ax1.plot(df_paciente_22_5_1_2019['hour'], df_paciente_22_5_1_2019['Measurement'])

# Agregar puntos que representen las mediciones de glucosa tomadas. "s" para minimizar los puntos
ax1.scatter(df_paciente_22_5_1_2019['hour'], df_paciente_22_5_1_2019['Measurement'], s=10, facecolors='none', edgecolors='b')

# Establecer los límites del eje x
ax1.set_xlim([0, 24])

# Establecer los límites del eje y
ax1.set_ylim([40, 500])

# Establecer las marcas del eje x
ax1.set_xticks(range(0, 25, 2))

# Establecer las etiquetas del eje x
ax1.set_xticklabels([f'{x}:00' for x in range(0, 25, 2)])

# Agregar una cuadrícula de líneas discontinuas al gráfico
ax1.grid(True, linestyle='dashed')

# Añadir un sombreado para el tiempo en rango de 70 a 180 en el eje y
ax1.axhspan(70, 180, facecolor='green', alpha=0.2)

# Agregar un título al gráfico
ax1.set_title('Día 5/10/2021') 

# Agregar etiquetas a los ejes x e y
ax1.set_xlabel('Horas')
ax1.set_ylabel('Nivel de Glucosa') 

# Crear una figura y un eje para la tabla
fig2, ax2 = plt.subplots()

# Crear una tabla que muestre los valores de glucosa para cada hora del día
table_data = []
for hour in range(0, 24, 2):
    measurements = df_paciente_22_5_1_2019[(df_paciente_22_5_1_2019['hour'] >= hour) & (df_paciente_22_5_1_2019['hour'] < hour + 2)]['Measurement'].values
    measurements_str = '\n'.join(map(str, measurements))
    table_data.append(measurements_str)

# Crear una tabla con 12 columnas en una posición específica
columns = ['0h - 2h', '2h - 4h', '4h - 6h', '6h - 8h', '8h - 10h', '10h - 12h', '12h - 14h', '14h - 16h', '16h - 18h', '18h - 20h', '20h - 22h', '22h - 24h']
table = Table(ax2, bbox=[0.059, -0.05 + 0.3, 1.9, 0.7])
#table = Table(ax2, bbox=[0.0, -0.05 + 0.3, 1.0, 0.15])

# Añadir las etiquetas de las columnas a la tabla
for i in range(len(columns)):
    table.add_cell(-1, i, width=0.15, height=0.1, text=columns[i], loc='center')

# Añadir los datos a la tabla
for i in range(len(table_data)):
    table.add_cell(0, i, width=0.15, height=0.9, text=table_data[i], loc='center')

ax2.add_table(table)

# Ocultar los ejes de la figura de la tabla
ax2.axis('off')

# Mostrar la gráfica y la tabla
plt.show()

Extraer paciente 22 en el mes 1, año 2019 las mediciones de glucosa según mañana, tarde y noche. Se analiza cuando la glucosa está en tiempo en rango¶

Mañana: 6:00 am y termina 12:00pm.

Tarde: 12:00 pm y termina 20h.

Noche: 20h y termina 6:00 am.

In [50]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df[(df['Patient_ID'] == 22) & (df['Measurement_date'].dt.year == 2019) & (df['Measurement_date'].dt.month == 1)]

# Extraer las mediciones de la mañana (entre las 6:00 am y las 12:00 pm)
morning_measurements = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 6) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 12)]['Measurement']

# Extraer las mediciones de la tarde (entre las 12:00 pm y las 8:00 pm)
afternoon_measurements = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 12) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 20)]['Measurement']

# Extraer las mediciones de la noche (entre las 8:00 pm y las 6:00 am del día siguiente)
night_measurements = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 20) | (df_paciente_22_1_2019['Measurement_time'].dt.hour < 6)]['Measurement']

# Calcular la media de las mediciones para cada período del día
morning_mean = morning_measurements.mean()
afternoon_mean = afternoon_measurements.mean()
night_mean = night_measurements.mean()

# Crear un nuevo DataFrame con los valores de las medias para cada período del día
df_medias = pd.DataFrame({'Período del día': ['Mañana (6am-12pm)', 'Tarde (12pm-8pm)', 'Noche (8pm-6am)'],
                          'Media': [f'{morning_mean:.2f}', f'{afternoon_mean:.2f}', f'{night_mean:.2f}']})
# Mostrar el resultado
print('Medias:')
display(df_medias)

# Mostrar el resultado
print('\nMediciones:')
# Crear un nuevo DataFrame con las mediciones de la mañana
df_morning = pd.DataFrame({'Mañana (6am-12pm)': morning_measurements})

# Crear un nuevo DataFrame con las mediciones de la tarde
df_afternoon = pd.DataFrame({'Tarde (12pm-8pm)': afternoon_measurements})

# Crear un nuevo DataFrame con las mediciones de la noche
df_night = pd.DataFrame({'Noche (8pm-6am)': night_measurements})

# Mostrar los resultados uno al lado del otro
from IPython.display import display_html

def display_side_by_side(*args):
    html_str=''
    for df in args:
        html_str+=df.to_html()
    display_html(html_str.replace('table','table style="display:inline"'),raw=True)

display_side_by_side(df_morning, df_afternoon, df_night)
Medias:
Período del día Media
0 Mañana (6am-12pm) 148.38
1 Tarde (12pm-8pm) 162.91
2 Noche (8pm-6am) 159.98
Mediciones:
Mañana (6am-12pm)
Datetime
2019-01-02 06:00:00 192
2019-01-02 06:15:00 188
2019-01-02 06:30:00 187
2019-01-02 06:45:00 183
2019-01-02 07:00:00 179
2019-01-02 07:15:00 178
2019-01-02 07:30:00 176
2019-01-02 07:45:00 174
2019-01-02 08:00:00 175
2019-01-03 06:00:00 132
2019-01-03 06:15:00 129
2019-01-03 06:30:00 128
2019-01-03 06:45:00 134
2019-01-03 07:00:00 136
2019-01-03 07:15:00 132
2019-01-03 07:30:00 126
2019-01-03 07:45:00 125
2019-01-03 08:00:00 132
2019-01-03 08:15:00 140
2019-01-03 08:30:00 143
2019-01-03 08:45:00 152
2019-01-03 09:00:00 156
2019-01-03 09:15:00 159
2019-01-03 09:30:00 166
2019-01-03 09:45:00 173
2019-01-03 10:00:00 173
2019-01-03 10:15:00 169
2019-01-03 10:30:00 161
2019-01-03 10:45:00 152
2019-01-03 11:00:00 144
2019-01-03 11:15:00 134
2019-01-03 11:30:00 124
2019-01-03 11:45:00 126
2019-01-04 06:00:00 166
2019-01-04 06:15:00 162
2019-01-04 06:30:00 162
2019-01-04 06:45:00 168
2019-01-04 07:00:00 171
2019-01-04 07:15:00 165
2019-01-04 07:30:00 155
2019-01-04 07:45:00 152
2019-01-04 08:00:00 152
2019-01-04 08:15:00 151
2019-01-04 08:30:00 147
2019-01-04 08:45:00 146
2019-01-04 09:00:00 147
2019-01-04 09:15:00 152
2019-01-04 09:30:00 155
2019-01-04 09:45:00 156
2019-01-04 10:00:00 160
2019-01-04 10:15:00 141
2019-01-04 10:30:00 136
2019-01-04 10:45:00 134
2019-01-04 11:00:00 134
2019-01-04 11:15:00 132
2019-01-04 11:30:00 125
2019-01-04 11:45:00 112
2019-01-05 06:00:00 146
2019-01-05 06:15:00 141
2019-01-05 06:30:00 137
2019-01-05 06:45:00 138
2019-01-05 07:00:00 135
2019-01-05 07:15:00 137
2019-01-05 07:30:00 136
2019-01-05 07:45:00 137
2019-01-05 08:00:00 138
2019-01-05 08:15:00 139
2019-01-05 08:30:00 138
2019-01-05 08:45:00 141
2019-01-05 09:00:00 141
2019-01-05 09:15:00 142
2019-01-05 09:30:00 146
2019-01-05 09:45:00 159
2019-01-05 10:00:00 166
2019-01-05 10:15:00 163
2019-01-05 10:30:00 158
2019-01-05 10:45:00 153
2019-01-05 11:00:00 148
2019-01-05 11:15:00 143
2019-01-05 11:30:00 145
2019-01-05 11:45:00 145
2019-01-06 06:00:00 152
2019-01-06 06:15:00 150
2019-01-06 06:30:00 149
2019-01-06 06:45:00 150
2019-01-06 07:00:00 160
2019-01-06 09:30:00 134
2019-01-06 09:45:00 127
2019-01-06 10:45:00 141
2019-01-06 11:00:00 147
2019-01-06 11:30:00 160
2019-01-07 06:00:00 143
2019-01-07 06:15:00 139
2019-01-07 06:30:00 138
2019-01-07 06:45:00 139
2019-01-07 07:00:00 146
2019-01-07 07:15:00 160
2019-01-07 07:30:00 173
2019-01-07 07:45:00 170
2019-01-07 08:00:00 170
2019-01-07 08:15:00 171
2019-01-07 08:30:00 170
2019-01-07 08:45:00 166
2019-01-07 09:00:00 163
2019-01-07 09:15:00 158
2019-01-07 09:30:00 155
2019-01-07 09:45:00 160
2019-01-07 10:00:00 169
2019-01-07 10:15:00 182
2019-01-07 10:30:00 184
2019-01-07 10:45:00 178
2019-01-07 11:00:00 171
2019-01-07 11:15:00 182
2019-01-08 06:00:00 162
2019-01-08 06:15:00 161
2019-01-08 06:30:00 160
2019-01-08 06:45:00 161
2019-01-08 07:00:00 160
2019-01-08 07:15:00 157
2019-01-08 07:30:00 158
2019-01-08 07:45:00 165
2019-01-08 08:00:00 166
2019-01-08 08:15:00 162
2019-01-08 08:30:00 155
2019-01-08 08:45:00 145
2019-01-08 09:00:00 138
2019-01-08 09:15:00 137
2019-01-08 09:30:00 134
2019-01-08 09:45:00 129
2019-01-08 10:00:00 136
2019-01-08 10:15:00 133
2019-01-08 10:30:00 144
2019-01-08 10:45:00 154
2019-01-08 11:00:00 164
2019-01-08 11:15:00 168
2019-01-08 11:30:00 165
2019-01-08 11:45:00 169
2019-01-09 06:15:00 165
2019-01-09 06:30:00 176
2019-01-09 07:15:00 191
2019-01-09 07:30:00 195
2019-01-09 07:45:00 196
2019-01-09 08:00:00 192
2019-01-09 08:15:00 192
2019-01-09 08:30:00 195
2019-01-09 08:45:00 195
2019-01-09 09:00:00 193
2019-01-09 09:15:00 194
2019-01-09 09:30:00 196
2019-01-09 09:45:00 198
2019-01-09 10:00:00 199
2019-01-09 10:15:00 195
2019-01-09 10:30:00 190
2019-01-09 10:45:00 186
2019-01-09 11:00:00 183
2019-01-09 11:15:00 180
2019-01-09 11:30:00 183
2019-01-09 11:45:00 188
2019-01-10 11:30:00 139
2019-01-10 11:45:00 162
2019-01-11 06:00:00 120
2019-01-11 06:15:00 120
2019-01-11 06:30:00 124
2019-01-11 06:45:00 129
2019-01-11 07:00:00 133
2019-01-11 07:15:00 126
2019-01-11 07:30:00 124
2019-01-11 07:45:00 136
2019-01-11 08:00:00 155
2019-01-11 08:15:00 168
2019-01-11 08:30:00 177
2019-01-11 08:45:00 179
2019-01-11 09:00:00 170
2019-01-11 09:15:00 156
2019-01-11 09:30:00 148
2019-01-11 09:45:00 148
2019-01-11 10:00:00 158
2019-01-11 10:15:00 172
2019-01-11 10:30:00 178
2019-01-11 10:45:00 178
2019-01-11 11:00:00 181
2019-01-11 11:15:00 226
2019-01-12 09:15:00 105
2019-01-12 09:30:00 106
2019-01-12 09:45:00 107
2019-01-12 10:00:00 109
2019-01-12 10:15:00 100
2019-01-12 10:30:00 98
2019-01-12 10:45:00 98
2019-01-12 11:00:00 106
2019-01-12 11:15:00 121
2019-01-12 11:30:00 144
2019-01-12 11:45:00 176
2019-01-13 08:30:00 134
2019-01-13 09:15:00 145
2019-01-13 09:30:00 146
2019-01-13 09:45:00 143
2019-01-13 10:00:00 148
2019-01-13 10:15:00 168
2019-01-13 10:30:00 181
2019-01-13 10:45:00 174
2019-01-13 11:30:00 151
2019-01-13 11:45:00 157
2019-01-15 06:30:00 112
2019-01-15 06:45:00 113
2019-01-15 07:00:00 105
2019-01-15 07:15:00 107
2019-01-15 07:30:00 104
2019-01-15 07:45:00 98
2019-01-15 08:00:00 97
2019-01-15 08:15:00 101
2019-01-15 08:30:00 109
2019-01-15 08:45:00 117
2019-01-15 09:00:00 130
2019-01-15 09:15:00 147
2019-01-15 09:30:00 149
2019-01-15 09:45:00 145
2019-01-15 10:00:00 145
2019-01-15 10:15:00 147
2019-01-15 10:30:00 148
2019-01-15 10:45:00 151
2019-01-15 11:00:00 152
2019-01-15 11:15:00 150
2019-01-15 11:30:00 145
2019-01-15 11:45:00 147
2019-01-16 11:30:00 146
2019-01-16 11:45:00 149
2019-01-19 06:00:00 108
2019-01-19 06:15:00 109
2019-01-19 06:30:00 109
2019-01-19 06:45:00 106
2019-01-19 07:00:00 103
2019-01-19 07:15:00 104
2019-01-19 07:30:00 108
2019-01-19 07:45:00 104
2019-01-19 08:00:00 95
2019-01-19 08:15:00 89
2019-01-19 08:30:00 90
2019-01-19 08:45:00 98
2019-01-19 09:00:00 111
2019-01-19 09:15:00 109
2019-01-19 10:45:00 143
2019-01-19 11:00:00 145
2019-01-20 06:00:00 181
2019-01-20 06:15:00 178
2019-01-20 06:30:00 184
2019-01-20 06:45:00 195
2019-01-20 07:00:00 196
2019-01-20 07:15:00 190
2019-01-20 07:30:00 187
2019-01-20 07:45:00 183
2019-01-20 08:00:00 177
2019-01-20 08:15:00 173
2019-01-20 08:30:00 171
2019-01-20 08:45:00 169
2019-01-20 09:00:00 170
2019-01-20 09:15:00 168
2019-01-20 09:30:00 164
2019-01-20 09:45:00 171
2019-01-20 10:00:00 173
2019-01-20 10:15:00 165
2019-01-20 10:30:00 162
2019-01-20 10:45:00 170
2019-01-20 11:15:00 185
2019-01-20 11:30:00 176
2019-01-20 11:45:00 165
2019-01-23 06:00:00 110
2019-01-23 06:15:00 109
2019-01-23 06:30:00 109
2019-01-23 06:45:00 114
2019-01-23 07:00:00 127
2019-01-23 07:15:00 135
2019-01-23 07:30:00 136
2019-01-23 07:45:00 138
2019-01-23 08:00:00 148
2019-01-23 08:15:00 155
2019-01-23 08:30:00 150
2019-01-23 08:45:00 145
2019-01-23 09:00:00 141
2019-01-23 09:15:00 136
2019-01-23 09:30:00 129
2019-01-23 09:45:00 126
2019-01-23 10:00:00 128
2019-01-23 10:15:00 128
2019-01-23 10:30:00 125
2019-01-23 10:45:00 131
2019-01-23 11:00:00 149
2019-01-23 11:15:00 167
2019-01-23 11:30:00 177
2019-01-23 11:45:00 189
2019-01-25 11:00:00 129
2019-01-25 11:15:00 133
2019-01-25 11:30:00 145
2019-01-25 11:45:00 163
2019-01-27 06:00:00 154
2019-01-27 06:15:00 157
2019-01-27 06:30:00 160
2019-01-27 06:45:00 150
2019-01-27 07:00:00 145
2019-01-27 07:15:00 147
2019-01-27 07:30:00 148
2019-01-27 07:45:00 145
2019-01-27 08:00:00 152
2019-01-27 08:15:00 163
2019-01-27 08:30:00 169
2019-01-27 08:45:00 169
2019-01-27 09:00:00 168
2019-01-27 09:15:00 160
2019-01-27 09:30:00 154
2019-01-27 09:45:00 146
2019-01-27 10:00:00 137
2019-01-27 10:15:00 127
2019-01-27 10:30:00 121
2019-01-27 10:45:00 138
2019-01-27 11:00:00 150
2019-01-27 11:15:00 177
2019-01-27 11:30:00 206
2019-01-27 11:45:00 222
2019-01-28 06:00:00 79
2019-01-28 06:15:00 79
2019-01-28 06:30:00 80
2019-01-28 06:45:00 85
2019-01-28 07:00:00 88
2019-01-28 07:15:00 97
2019-01-28 07:30:00 104
2019-01-28 07:45:00 103
2019-01-29 06:00:00 138
2019-01-29 06:45:00 146
2019-01-29 07:15:00 138
2019-01-29 07:30:00 139
2019-01-29 07:45:00 141
2019-01-29 08:00:00 143
2019-01-29 08:15:00 133
2019-01-29 08:30:00 119
2019-01-29 09:15:00 95
2019-01-29 09:30:00 104
2019-01-29 09:45:00 118
2019-01-29 10:00:00 130
2019-01-29 10:15:00 140
2019-01-29 10:30:00 152
2019-01-31 06:00:00 121
2019-01-31 06:15:00 120
2019-01-31 06:30:00 116
2019-01-31 06:45:00 122
2019-01-31 07:00:00 156
2019-01-31 07:15:00 161
2019-01-31 07:30:00 167
2019-01-31 08:30:00 202
2019-01-31 08:45:00 195
2019-01-31 09:00:00 187
2019-01-31 10:00:00 148
2019-01-31 10:15:00 141
2019-01-31 10:30:00 131
2019-01-31 10:45:00 119
2019-01-31 11:30:00 127
Tarde (12pm-8pm)
Datetime
2019-01-01 12:30:00 153
2019-01-01 12:45:00 144
2019-01-01 13:00:00 143
2019-01-01 13:15:00 148
2019-01-01 13:30:00 144
2019-01-01 13:45:00 140
2019-01-01 14:00:00 139
2019-01-01 14:15:00 137
2019-01-01 14:30:00 132
2019-01-01 14:45:00 137
2019-01-01 15:00:00 149
2019-01-01 15:15:00 160
2019-01-01 15:30:00 158
2019-01-01 15:45:00 146
2019-01-01 16:00:00 142
2019-01-01 16:15:00 147
2019-01-01 16:30:00 146
2019-01-01 16:45:00 148
2019-01-01 17:00:00 151
2019-01-01 17:15:00 147
2019-01-01 17:30:00 145
2019-01-01 17:45:00 124
2019-01-01 18:00:00 125
2019-01-01 18:15:00 127
2019-01-01 18:30:00 130
2019-01-01 18:45:00 132
2019-01-01 19:00:00 131
2019-01-01 19:15:00 128
2019-01-01 19:30:00 123
2019-01-01 19:45:00 117
2019-01-02 13:15:00 130
2019-01-02 13:30:00 124
2019-01-02 13:45:00 120
2019-01-02 14:00:00 115
2019-01-02 14:15:00 120
2019-01-02 14:30:00 131
2019-01-02 14:45:00 140
2019-01-02 15:00:00 142
2019-01-02 15:15:00 143
2019-01-02 15:30:00 146
2019-01-02 15:45:00 152
2019-01-02 16:00:00 153
2019-01-02 16:15:00 153
2019-01-02 16:30:00 158
2019-01-02 16:45:00 162
2019-01-02 17:00:00 175
2019-01-02 17:15:00 184
2019-01-02 17:30:00 190
2019-01-02 17:45:00 191
2019-01-02 18:00:00 188
2019-01-02 18:15:00 181
2019-01-02 18:30:00 177
2019-01-02 18:45:00 167
2019-01-02 19:00:00 157
2019-01-02 19:15:00 155
2019-01-02 19:30:00 155
2019-01-02 19:45:00 155
2019-01-03 12:00:00 136
2019-01-03 12:15:00 140
2019-01-03 12:30:00 147
2019-01-03 12:45:00 157
2019-01-03 13:00:00 163
2019-01-03 13:15:00 162
2019-01-03 13:30:00 163
2019-01-03 13:45:00 159
2019-01-03 14:00:00 141
2019-01-03 14:15:00 131
2019-01-03 14:30:00 124
2019-01-03 14:45:00 122
2019-01-03 15:00:00 124
2019-01-03 15:15:00 127
2019-01-03 15:30:00 138
2019-01-03 15:45:00 149
2019-01-03 16:00:00 168
2019-01-03 16:15:00 165
2019-01-03 16:30:00 158
2019-01-03 16:45:00 154
2019-01-03 17:00:00 154
2019-01-03 17:15:00 157
2019-01-03 17:30:00 158
2019-01-03 17:45:00 153
2019-01-03 18:00:00 148
2019-01-03 18:15:00 147
2019-01-03 18:30:00 152
2019-01-03 18:45:00 156
2019-01-03 19:00:00 162
2019-01-03 19:15:00 168
2019-01-03 19:30:00 171
2019-01-03 19:45:00 174
2019-01-04 12:00:00 108
2019-01-04 12:15:00 109
2019-01-04 12:30:00 111
2019-01-04 12:45:00 110
2019-01-04 13:00:00 108
2019-01-04 13:15:00 130
2019-01-04 13:30:00 128
2019-01-04 13:45:00 124
2019-01-04 14:00:00 120
2019-01-04 14:15:00 119
2019-01-04 14:30:00 121
2019-01-04 14:45:00 129
2019-01-04 15:00:00 148
2019-01-04 15:15:00 161
2019-01-04 15:30:00 164
2019-01-04 15:45:00 165
2019-01-04 16:00:00 175
2019-01-04 16:15:00 188
2019-01-04 16:30:00 200
2019-01-04 16:45:00 198
2019-01-04 17:00:00 200
2019-01-04 17:15:00 195
2019-01-04 17:30:00 202
2019-01-04 17:45:00 215
2019-01-04 18:00:00 218
2019-01-04 18:15:00 214
2019-01-04 18:30:00 213
2019-01-04 18:45:00 213
2019-01-04 19:00:00 192
2019-01-04 19:15:00 188
2019-01-04 19:30:00 178
2019-01-04 19:45:00 174
2019-01-05 12:00:00 142
2019-01-05 12:15:00 140
2019-01-05 12:30:00 143
2019-01-05 12:45:00 146
2019-01-05 13:00:00 154
2019-01-05 13:15:00 160
2019-01-05 13:30:00 158
2019-01-05 13:45:00 149
2019-01-05 14:00:00 138
2019-01-05 14:15:00 131
2019-01-05 14:30:00 125
2019-01-05 14:45:00 106
2019-01-05 15:00:00 124
2019-01-05 15:15:00 145
2019-01-05 15:30:00 161
2019-01-05 15:45:00 162
2019-01-05 16:00:00 168
2019-01-05 16:15:00 172
2019-01-05 16:30:00 173
2019-01-05 16:45:00 174
2019-01-05 17:00:00 174
2019-01-05 17:15:00 167
2019-01-05 17:30:00 159
2019-01-05 17:45:00 157
2019-01-05 18:00:00 152
2019-01-05 18:15:00 148
2019-01-05 18:30:00 149
2019-01-05 18:45:00 153
2019-01-05 19:00:00 158
2019-01-05 19:15:00 162
2019-01-05 19:30:00 193
2019-01-05 19:45:00 195
2019-01-06 12:15:00 120
2019-01-06 12:30:00 118
2019-01-06 14:00:00 128
2019-01-06 14:15:00 146
2019-01-06 14:30:00 156
2019-01-06 14:45:00 166
2019-01-06 15:00:00 185
2019-01-06 15:15:00 203
2019-01-06 15:30:00 262
2019-01-06 15:45:00 219
2019-01-06 16:00:00 218
2019-01-06 16:15:00 226
2019-01-06 16:30:00 235
2019-01-06 16:45:00 237
2019-01-06 17:00:00 237
2019-01-06 17:15:00 232
2019-01-06 17:30:00 226
2019-01-06 17:45:00 224
2019-01-06 18:00:00 228
2019-01-06 18:15:00 236
2019-01-06 18:30:00 244
2019-01-06 18:45:00 246
2019-01-06 19:00:00 249
2019-01-06 19:15:00 254
2019-01-06 19:30:00 255
2019-01-06 19:45:00 252
2019-01-07 13:30:00 140
2019-01-07 13:45:00 137
2019-01-07 14:00:00 133
2019-01-07 14:15:00 136
2019-01-07 15:15:00 165
2019-01-07 15:30:00 170
2019-01-07 15:45:00 169
2019-01-07 16:00:00 164
2019-01-07 17:00:00 136
2019-01-07 17:15:00 150
2019-01-07 17:30:00 157
2019-01-07 17:45:00 161
2019-01-07 18:00:00 167
2019-01-07 18:15:00 172
2019-01-07 18:30:00 179
2019-01-07 18:45:00 188
2019-01-07 19:00:00 191
2019-01-07 19:15:00 184
2019-01-07 19:30:00 181
2019-01-07 19:45:00 181
2019-01-08 12:00:00 175
2019-01-08 12:15:00 200
2019-01-08 12:30:00 186
2019-01-08 13:15:00 173
2019-01-08 13:30:00 167
2019-01-08 14:15:00 148
2019-01-08 14:45:00 134
2019-01-08 15:30:00 198
2019-01-08 15:45:00 199
2019-01-08 16:30:00 189
2019-01-08 16:45:00 189
2019-01-08 17:30:00 142
2019-01-08 17:45:00 133
2019-01-08 18:00:00 128
2019-01-08 18:15:00 126
2019-01-08 18:30:00 130
2019-01-08 18:45:00 136
2019-01-08 19:00:00 142
2019-01-08 19:15:00 146
2019-01-09 12:00:00 192
2019-01-09 12:15:00 200
2019-01-09 12:30:00 214
2019-01-09 12:45:00 218
2019-01-09 13:00:00 209
2019-01-09 13:15:00 200
2019-01-09 13:30:00 192
2019-01-09 13:45:00 182
2019-01-09 14:00:00 176
2019-01-09 14:15:00 181
2019-01-09 14:30:00 190
2019-01-09 14:45:00 196
2019-01-09 15:00:00 209
2019-01-09 15:15:00 204
2019-01-09 15:30:00 209
2019-01-09 15:45:00 211
2019-01-09 16:30:00 181
2019-01-09 16:45:00 171
2019-01-09 17:30:00 147
2019-01-09 17:45:00 150
2019-01-09 18:15:00 168
2019-01-09 19:15:00 216
2019-01-09 19:45:00 214
2019-01-10 12:00:00 170
2019-01-10 12:15:00 180
2019-01-10 12:30:00 183
2019-01-10 12:45:00 178
2019-01-10 13:00:00 174
2019-01-10 13:15:00 173
2019-01-10 13:30:00 173
2019-01-10 13:45:00 169
2019-01-10 14:00:00 184
2019-01-10 14:15:00 153
2019-01-10 14:45:00 140
2019-01-11 18:00:00 151
2019-01-11 18:15:00 160
2019-01-11 18:30:00 160
2019-01-11 18:45:00 153
2019-01-12 12:00:00 195
2019-01-12 12:15:00 192
2019-01-12 12:30:00 184
2019-01-12 12:45:00 172
2019-01-12 13:00:00 158
2019-01-12 13:15:00 148
2019-01-12 13:30:00 142
2019-01-12 13:45:00 137
2019-01-12 14:00:00 132
2019-01-12 14:15:00 118
2019-01-12 14:30:00 108
2019-01-12 14:45:00 112
2019-01-12 15:00:00 124
2019-01-12 15:15:00 130
2019-01-12 15:30:00 143
2019-01-12 15:45:00 174
2019-01-12 16:00:00 201
2019-01-12 16:15:00 205
2019-01-12 16:30:00 206
2019-01-12 16:45:00 206
2019-01-12 17:00:00 201
2019-01-12 17:15:00 191
2019-01-12 17:30:00 182
2019-01-12 17:45:00 175
2019-01-12 18:00:00 170
2019-01-12 18:15:00 166
2019-01-12 18:30:00 169
2019-01-12 18:45:00 180
2019-01-12 19:00:00 195
2019-01-13 12:30:00 159
2019-01-13 12:45:00 156
2019-01-13 13:00:00 150
2019-01-13 13:15:00 148
2019-01-13 13:30:00 151
2019-01-13 13:45:00 160
2019-01-13 14:00:00 173
2019-01-13 14:15:00 184
2019-01-13 14:30:00 188
2019-01-13 14:45:00 196
2019-01-13 15:00:00 207
2019-01-13 15:15:00 216
2019-01-13 15:30:00 223
2019-01-13 15:45:00 230
2019-01-13 16:00:00 238
2019-01-13 16:15:00 241
2019-01-13 16:30:00 242
2019-01-13 16:45:00 237
2019-01-13 17:00:00 228
2019-01-13 17:15:00 221
2019-01-13 17:30:00 213
2019-01-13 17:45:00 205
2019-01-13 18:00:00 199
2019-01-13 18:15:00 193
2019-01-13 18:30:00 185
2019-01-13 18:45:00 173
2019-01-13 19:00:00 165
2019-01-13 19:15:00 161
2019-01-13 19:30:00 152
2019-01-13 19:45:00 146
2019-01-15 12:00:00 155
2019-01-15 12:15:00 160
2019-01-15 12:30:00 164
2019-01-15 12:45:00 164
2019-01-15 13:00:00 157
2019-01-15 13:15:00 154
2019-01-15 13:30:00 154
2019-01-15 13:45:00 151
2019-01-15 14:00:00 144
2019-01-15 14:30:00 167
2019-01-15 14:45:00 152
2019-01-15 16:00:00 152
2019-01-15 16:30:00 128
2019-01-15 16:45:00 123
2019-01-15 17:00:00 116
2019-01-15 17:15:00 111
2019-01-15 17:30:00 105
2019-01-15 17:45:00 100
2019-01-15 18:00:00 101
2019-01-15 18:15:00 105
2019-01-15 18:30:00 105
2019-01-15 18:45:00 104
2019-01-15 19:00:00 104
2019-01-15 19:15:00 102
2019-01-15 19:30:00 101
2019-01-15 19:45:00 103
2019-01-16 12:00:00 154
2019-01-16 12:15:00 154
2019-01-16 12:30:00 154
2019-01-16 12:45:00 167
2019-01-16 13:00:00 173
2019-01-16 13:15:00 176
2019-01-16 13:30:00 188
2019-01-16 13:45:00 183
2019-01-16 14:00:00 170
2019-01-16 14:15:00 164
2019-01-16 14:30:00 138
2019-01-16 14:45:00 133
2019-01-16 15:00:00 132
2019-01-16 15:15:00 139
2019-01-16 15:30:00 143
2019-01-16 15:45:00 136
2019-01-16 16:00:00 126
2019-01-16 16:15:00 116
2019-01-16 16:30:00 108
2019-01-16 16:45:00 103
2019-01-16 17:00:00 102
2019-01-16 17:15:00 104
2019-01-16 17:30:00 106
2019-01-16 17:45:00 112
2019-01-16 18:00:00 119
2019-01-16 18:15:00 128
2019-01-16 18:30:00 142
2019-01-16 18:45:00 153
2019-01-16 19:00:00 158
2019-01-16 19:15:00 158
2019-01-16 19:30:00 156
2019-01-16 19:45:00 160
2019-01-19 12:30:00 176
2019-01-19 12:45:00 172
2019-01-19 13:15:00 161
2019-01-19 13:30:00 149
2019-01-19 13:45:00 140
2019-01-20 12:00:00 160
2019-01-20 12:15:00 158
2019-01-20 12:30:00 152
2019-01-20 12:45:00 145
2019-01-20 13:00:00 139
2019-01-20 13:15:00 136
2019-01-20 13:30:00 135
2019-01-20 13:45:00 138
2019-01-20 14:00:00 138
2019-01-20 14:15:00 132
2019-01-20 14:30:00 120
2019-01-20 14:45:00 116
2019-01-20 16:15:00 144
2019-01-20 16:30:00 166
2019-01-20 16:45:00 171
2019-01-20 17:00:00 174
2019-01-20 17:15:00 178
2019-01-20 17:30:00 184
2019-01-20 17:45:00 187
2019-01-20 18:00:00 188
2019-01-20 18:15:00 194
2019-01-20 18:30:00 207
2019-01-20 18:45:00 223
2019-01-20 19:00:00 232
2019-01-20 19:15:00 228
2019-01-20 19:30:00 223
2019-01-20 19:45:00 217
2019-01-21 17:30:00 190
2019-01-21 17:45:00 186
2019-01-21 18:00:00 183
2019-01-21 18:15:00 191
2019-01-21 18:30:00 197
2019-01-21 18:45:00 202
2019-01-21 19:00:00 204
2019-01-21 19:15:00 182
2019-01-21 19:30:00 182
2019-01-21 19:45:00 176
2019-01-23 12:00:00 199
2019-01-23 12:15:00 202
2019-01-23 12:30:00 201
2019-01-23 12:45:00 195
2019-01-23 13:00:00 184
2019-01-23 13:15:00 175
2019-01-23 13:30:00 175
2019-01-23 13:45:00 174
2019-01-23 14:00:00 170
2019-01-23 14:15:00 171
2019-01-23 14:30:00 172
2019-01-23 14:45:00 177
2019-01-23 15:00:00 169
2019-01-23 15:15:00 173
2019-01-23 15:30:00 173
2019-01-23 15:45:00 174
2019-01-23 16:00:00 171
2019-01-23 16:15:00 160
2019-01-23 16:30:00 149
2019-01-23 16:45:00 138
2019-01-23 17:00:00 132
2019-01-25 12:00:00 180
2019-01-25 12:15:00 198
2019-01-25 12:30:00 210
2019-01-25 12:45:00 209
2019-01-25 13:00:00 203
2019-01-25 13:15:00 201
2019-01-25 13:30:00 201
2019-01-25 13:45:00 205
2019-01-25 14:00:00 207
2019-01-25 14:15:00 200
2019-01-25 14:30:00 193
2019-01-25 14:45:00 192
2019-01-25 15:00:00 191
2019-01-25 15:15:00 188
2019-01-25 15:30:00 182
2019-01-25 15:45:00 173
2019-01-25 16:00:00 161
2019-01-25 16:15:00 141
2019-01-25 16:30:00 114
2019-01-25 16:45:00 94
2019-01-25 17:00:00 86
2019-01-25 17:15:00 80
2019-01-25 17:30:00 82
2019-01-25 17:45:00 91
2019-01-25 18:00:00 97
2019-01-25 18:15:00 103
2019-01-25 18:30:00 115
2019-01-25 18:45:00 133
2019-01-25 19:00:00 144
2019-01-25 19:15:00 148
2019-01-25 19:30:00 148
2019-01-25 19:45:00 151
2019-01-27 12:00:00 229
2019-01-27 12:15:00 236
2019-01-27 12:30:00 231
2019-01-27 12:45:00 220
2019-01-27 13:00:00 210
2019-01-27 13:15:00 199
2019-01-27 13:30:00 184
2019-01-27 13:45:00 169
2019-01-27 14:00:00 157
2019-01-27 14:15:00 148
2019-01-27 14:30:00 141
2019-01-27 14:45:00 140
2019-01-28 14:45:00 175
2019-01-28 15:00:00 172
2019-01-28 15:15:00 166
2019-01-28 15:45:00 162
2019-01-28 16:00:00 158
2019-01-28 16:30:00 138
2019-01-28 16:45:00 124
2019-01-28 17:00:00 114
2019-01-28 17:15:00 114
2019-01-28 17:30:00 116
2019-01-28 17:45:00 113
2019-01-28 18:00:00 114
2019-01-28 18:15:00 120
2019-01-28 18:30:00 127
2019-01-28 18:45:00 132
2019-01-28 19:00:00 146
2019-01-28 19:15:00 167
2019-01-28 19:30:00 169
2019-01-28 19:45:00 162
2019-01-31 12:00:00 135
2019-01-31 12:15:00 135
2019-01-31 12:30:00 135
2019-01-31 12:45:00 138
2019-01-31 13:00:00 142
2019-01-31 13:15:00 139
2019-01-31 13:30:00 135
2019-01-31 13:45:00 132
2019-01-31 14:00:00 134
2019-01-31 14:45:00 180
2019-01-31 15:30:00 175
2019-01-31 15:45:00 176
2019-01-31 16:00:00 189
2019-01-31 16:15:00 192
2019-01-31 19:45:00 145
Noche (8pm-6am)
Datetime
2019-01-01 00:00:00 194
2019-01-01 00:15:00 205
2019-01-01 00:30:00 216
2019-01-01 00:45:00 217
2019-01-01 01:00:00 225
2019-01-01 01:15:00 233
2019-01-01 01:30:00 236
2019-01-01 01:45:00 231
2019-01-01 02:00:00 230
2019-01-01 02:15:00 226
2019-01-01 02:30:00 217
2019-01-01 02:45:00 214
2019-01-01 03:00:00 216
2019-01-01 03:15:00 215
2019-01-01 03:30:00 210
2019-01-01 03:45:00 185
2019-01-01 20:00:00 116
2019-01-01 20:15:00 121
2019-01-01 20:30:00 128
2019-01-01 20:45:00 135
2019-01-01 21:00:00 138
2019-01-01 21:15:00 136
2019-01-01 21:30:00 132
2019-01-01 21:45:00 133
2019-01-01 22:00:00 128
2019-01-01 22:15:00 110
2019-01-01 22:30:00 116
2019-01-01 23:15:00 111
2019-01-01 23:30:00 109
2019-01-01 23:45:00 107
2019-01-02 00:00:00 107
2019-01-02 00:15:00 113
2019-01-02 00:30:00 124
2019-01-02 01:00:00 131
2019-01-02 01:15:00 117
2019-01-02 01:30:00 132
2019-01-02 01:45:00 130
2019-01-02 02:00:00 133
2019-01-02 02:15:00 135
2019-01-02 02:30:00 137
2019-01-02 02:45:00 138
2019-01-02 03:00:00 139
2019-01-02 03:15:00 155
2019-01-02 03:30:00 182
2019-01-02 03:45:00 190
2019-01-02 04:00:00 195
2019-01-02 04:15:00 201
2019-01-02 04:30:00 202
2019-01-02 04:45:00 195
2019-01-02 05:00:00 192
2019-01-02 05:15:00 197
2019-01-02 05:30:00 199
2019-01-02 05:45:00 198
2019-01-02 20:00:00 157
2019-01-02 20:15:00 158
2019-01-02 20:30:00 156
2019-01-02 20:45:00 155
2019-01-02 21:00:00 153
2019-01-02 21:15:00 152
2019-01-02 21:30:00 154
2019-01-02 21:45:00 156
2019-01-02 22:00:00 154
2019-01-02 22:15:00 152
2019-01-02 22:30:00 96
2019-01-02 22:45:00 95
2019-01-02 23:00:00 96
2019-01-02 23:15:00 101
2019-01-02 23:30:00 100
2019-01-02 23:45:00 93
2019-01-03 02:30:00 142
2019-01-03 02:45:00 138
2019-01-03 03:00:00 142
2019-01-03 03:15:00 142
2019-01-03 03:30:00 146
2019-01-03 03:45:00 153
2019-01-03 04:00:00 153
2019-01-03 04:15:00 151
2019-01-03 04:30:00 146
2019-01-03 04:45:00 142
2019-01-03 05:00:00 139
2019-01-03 05:15:00 136
2019-01-03 05:30:00 135
2019-01-03 05:45:00 134
2019-01-03 20:00:00 178
2019-01-03 20:15:00 180
2019-01-03 20:30:00 180
2019-01-03 20:45:00 178
2019-01-03 21:00:00 169
2019-01-03 21:15:00 161
2019-01-03 21:30:00 155
2019-01-03 21:45:00 148
2019-01-03 22:00:00 144
2019-01-03 22:15:00 143
2019-01-03 22:30:00 144
2019-01-03 22:45:00 132
2019-01-03 23:00:00 154
2019-01-03 23:15:00 145
2019-01-03 23:30:00 127
2019-01-04 00:45:00 130
2019-01-04 01:00:00 140
2019-01-04 01:15:00 159
2019-01-04 01:30:00 176
2019-01-04 01:45:00 180
2019-01-04 02:00:00 175
2019-01-04 02:15:00 176
2019-01-04 02:30:00 200
2019-01-04 02:45:00 200
2019-01-04 03:00:00 200
2019-01-04 03:15:00 202
2019-01-04 03:30:00 181
2019-01-04 03:45:00 178
2019-01-04 04:00:00 176
2019-01-04 04:15:00 175
2019-01-04 04:30:00 175
2019-01-04 04:45:00 172
2019-01-04 05:00:00 170
2019-01-04 05:15:00 169
2019-01-04 05:30:00 165
2019-01-04 05:45:00 163
2019-01-04 20:00:00 174
2019-01-04 20:15:00 173
2019-01-04 20:30:00 175
2019-01-04 20:45:00 181
2019-01-04 21:00:00 192
2019-01-04 21:15:00 199
2019-01-04 21:30:00 203
2019-01-04 21:45:00 210
2019-01-04 22:00:00 229
2019-01-04 22:15:00 229
2019-01-04 22:30:00 226
2019-01-04 22:45:00 228
2019-01-04 23:00:00 241
2019-01-04 23:15:00 231
2019-01-04 23:30:00 205
2019-01-04 23:45:00 210
2019-01-05 00:00:00 216
2019-01-05 00:15:00 219
2019-01-05 00:30:00 223
2019-01-05 00:45:00 225
2019-01-05 01:00:00 190
2019-01-05 01:15:00 185
2019-01-05 01:30:00 185
2019-01-05 01:45:00 185
2019-01-05 02:00:00 179
2019-01-05 02:15:00 169
2019-01-05 02:30:00 161
2019-01-05 02:45:00 152
2019-01-05 03:00:00 144
2019-01-05 03:15:00 142
2019-01-05 03:30:00 143
2019-01-05 03:45:00 146
2019-01-05 04:00:00 140
2019-01-05 04:15:00 169
2019-01-05 04:30:00 164
2019-01-05 04:45:00 161
2019-01-05 05:00:00 160
2019-01-05 05:15:00 156
2019-01-05 05:30:00 152
2019-01-05 05:45:00 148
2019-01-05 20:00:00 192
2019-01-05 20:15:00 191
2019-01-05 20:30:00 195
2019-01-05 20:45:00 199
2019-01-05 21:00:00 199
2019-01-05 21:15:00 203
2019-01-05 21:30:00 215
2019-01-05 21:45:00 181
2019-01-05 22:00:00 202
2019-01-05 22:15:00 213
2019-01-05 22:30:00 211
2019-01-05 22:45:00 224
2019-01-05 23:00:00 222
2019-01-05 23:15:00 193
2019-01-05 23:30:00 190
2019-01-05 23:45:00 189
2019-01-06 00:00:00 193
2019-01-06 00:15:00 198
2019-01-06 00:30:00 193
2019-01-06 00:45:00 189
2019-01-06 01:00:00 192
2019-01-06 01:15:00 192
2019-01-06 01:30:00 206
2019-01-06 01:45:00 198
2019-01-06 02:00:00 186
2019-01-06 02:15:00 180
2019-01-06 02:30:00 177
2019-01-06 02:45:00 172
2019-01-06 03:00:00 166
2019-01-06 03:15:00 157
2019-01-06 03:30:00 151
2019-01-06 03:45:00 149
2019-01-06 04:00:00 148
2019-01-06 04:15:00 149
2019-01-06 04:30:00 149
2019-01-06 04:45:00 150
2019-01-06 05:00:00 150
2019-01-06 05:15:00 152
2019-01-06 05:30:00 154
2019-01-06 05:45:00 154
2019-01-06 20:00:00 247
2019-01-06 20:15:00 238
2019-01-06 20:30:00 228
2019-01-06 20:45:00 216
2019-01-06 21:00:00 197
2019-01-06 21:15:00 184
2019-01-06 21:30:00 160
2019-01-06 21:45:00 159
2019-01-06 22:00:00 154
2019-01-06 22:15:00 142
2019-01-06 22:30:00 123
2019-01-06 22:45:00 110
2019-01-06 23:00:00 106
2019-01-06 23:15:00 105
2019-01-06 23:30:00 93
2019-01-06 23:45:00 86
2019-01-07 00:00:00 80
2019-01-07 00:15:00 80
2019-01-07 00:30:00 83
2019-01-07 00:45:00 80
2019-01-07 01:00:00 74
2019-01-07 01:15:00 72
2019-01-07 01:30:00 73
2019-01-07 01:45:00 79
2019-01-07 02:00:00 118
2019-01-07 02:15:00 119
2019-01-07 02:30:00 120
2019-01-07 02:45:00 122
2019-01-07 03:00:00 133
2019-01-07 03:15:00 133
2019-01-07 03:30:00 134
2019-01-07 03:45:00 138
2019-01-07 04:00:00 146
2019-01-07 04:15:00 151
2019-01-07 04:30:00 150
2019-01-07 04:45:00 150
2019-01-07 05:00:00 148
2019-01-07 05:15:00 144
2019-01-07 05:30:00 142
2019-01-07 05:45:00 143
2019-01-07 20:00:00 180
2019-01-07 20:15:00 180
2019-01-07 20:30:00 175
2019-01-07 20:45:00 167
2019-01-07 21:00:00 158
2019-01-07 21:15:00 150
2019-01-07 21:30:00 145
2019-01-07 21:45:00 145
2019-01-07 22:00:00 146
2019-01-07 22:15:00 154
2019-01-07 22:30:00 147
2019-01-07 22:45:00 140
2019-01-07 23:00:00 136
2019-01-07 23:15:00 132
2019-01-07 23:30:00 129
2019-01-07 23:45:00 127
2019-01-08 00:00:00 124
2019-01-08 00:15:00 122
2019-01-08 00:30:00 120
2019-01-08 00:45:00 121
2019-01-08 01:00:00 121
2019-01-08 01:15:00 125
2019-01-08 01:30:00 134
2019-01-08 01:45:00 138
2019-01-08 02:00:00 143
2019-01-08 02:15:00 146
2019-01-08 02:30:00 147
2019-01-08 02:45:00 151
2019-01-08 03:00:00 206
2019-01-08 03:15:00 203
2019-01-08 03:30:00 201
2019-01-08 03:45:00 198
2019-01-08 04:00:00 179
2019-01-08 04:15:00 175
2019-01-08 04:30:00 171
2019-01-08 04:45:00 170
2019-01-08 05:00:00 171
2019-01-08 05:15:00 173
2019-01-08 05:30:00 173
2019-01-08 05:45:00 168
2019-01-08 20:15:00 141
2019-01-08 20:30:00 132
2019-01-08 20:45:00 124
2019-01-08 21:00:00 120
2019-01-08 21:15:00 123
2019-01-08 21:30:00 124
2019-01-08 21:45:00 132
2019-01-08 22:00:00 137
2019-01-08 22:15:00 145
2019-01-08 22:30:00 146
2019-01-08 22:45:00 148
2019-01-08 23:00:00 148
2019-01-08 23:15:00 138
2019-01-08 23:30:00 139
2019-01-08 23:45:00 156
2019-01-09 00:00:00 159
2019-01-09 00:15:00 160
2019-01-09 00:30:00 160
2019-01-09 00:45:00 158
2019-01-09 01:00:00 157
2019-01-09 01:15:00 162
2019-01-09 01:30:00 171
2019-01-09 01:45:00 181
2019-01-09 02:00:00 182
2019-01-09 02:15:00 163
2019-01-09 02:30:00 155
2019-01-09 02:45:00 149
2019-01-09 03:00:00 147
2019-01-09 03:15:00 148
2019-01-09 03:30:00 145
2019-01-09 03:45:00 145
2019-01-09 04:00:00 148
2019-01-09 04:45:00 159
2019-01-09 05:15:00 156
2019-01-09 05:30:00 157
2019-01-09 20:00:00 209
2019-01-09 20:15:00 207
2019-01-09 20:30:00 207
2019-01-09 20:45:00 203
2019-01-09 21:00:00 195
2019-01-09 21:15:00 183
2019-01-09 21:30:00 164
2019-01-09 21:45:00 148
2019-01-09 22:00:00 137
2019-01-09 22:15:00 127
2019-01-09 22:30:00 116
2019-01-09 22:45:00 112
2019-01-09 23:00:00 118
2019-01-09 23:15:00 141
2019-01-09 23:30:00 144
2019-01-09 23:45:00 164
2019-01-10 00:00:00 167
2019-01-10 00:15:00 152
2019-01-10 02:30:00 161
2019-01-10 03:00:00 168
2019-01-10 03:15:00 166
2019-01-10 03:30:00 161
2019-01-10 03:45:00 155
2019-01-10 23:00:00 153
2019-01-10 23:15:00 168
2019-01-10 23:30:00 178
2019-01-10 23:45:00 178
2019-01-11 00:00:00 179
2019-01-11 00:15:00 178
2019-01-11 00:30:00 171
2019-01-11 00:45:00 168
2019-01-11 01:00:00 162
2019-01-11 01:15:00 153
2019-01-11 01:30:00 147
2019-01-11 01:45:00 143
2019-01-11 02:00:00 142
2019-01-11 02:15:00 140
2019-01-11 02:30:00 138
2019-01-11 02:45:00 136
2019-01-11 03:00:00 134
2019-01-11 03:15:00 132
2019-01-11 03:30:00 131
2019-01-11 03:45:00 130
2019-01-11 04:00:00 129
2019-01-11 04:15:00 132
2019-01-11 04:30:00 131
2019-01-11 04:45:00 130
2019-01-11 05:00:00 128
2019-01-11 05:15:00 126
2019-01-11 05:30:00 126
2019-01-11 05:45:00 123
2019-01-11 20:15:00 140
2019-01-11 20:30:00 156
2019-01-11 20:45:00 164
2019-01-11 21:00:00 179
2019-01-11 21:15:00 185
2019-01-11 22:15:00 298
2019-01-11 22:30:00 308
2019-01-11 22:45:00 312
2019-01-11 23:00:00 315
2019-01-11 23:15:00 331
2019-01-11 23:30:00 339
2019-01-11 23:45:00 325
2019-01-12 00:00:00 310
2019-01-12 00:15:00 284
2019-01-12 00:30:00 181
2019-01-12 00:45:00 162
2019-01-12 01:00:00 143
2019-01-12 01:15:00 122
2019-01-12 23:00:00 120
2019-01-12 23:15:00 122
2019-01-12 23:30:00 124
2019-01-12 23:45:00 107
2019-01-13 00:00:00 118
2019-01-13 00:15:00 123
2019-01-13 00:30:00 127
2019-01-13 00:45:00 124
2019-01-13 01:00:00 126
2019-01-13 01:15:00 139
2019-01-13 20:00:00 145
2019-01-13 20:15:00 141
2019-01-13 20:30:00 135
2019-01-13 20:45:00 128
2019-01-13 21:00:00 122
2019-01-13 21:15:00 119
2019-01-13 21:30:00 111
2019-01-13 21:45:00 95
2019-01-13 22:00:00 94
2019-01-13 22:15:00 106
2019-01-13 22:30:00 138
2019-01-13 22:45:00 164
2019-01-13 23:00:00 190
2019-01-13 23:15:00 171
2019-01-13 23:30:00 179
2019-01-15 20:00:00 102
2019-01-15 20:15:00 104
2019-01-15 20:30:00 109
2019-01-15 20:45:00 115
2019-01-15 21:00:00 117
2019-01-15 21:15:00 114
2019-01-15 21:30:00 107
2019-01-15 21:45:00 101
2019-01-15 22:00:00 101
2019-01-15 22:15:00 80
2019-01-15 22:30:00 96
2019-01-15 22:45:00 115
2019-01-15 23:00:00 120
2019-01-16 20:00:00 164
2019-01-16 20:15:00 160
2019-01-16 20:30:00 146
2019-01-16 20:45:00 133
2019-01-16 21:00:00 129
2019-01-16 21:15:00 124
2019-01-16 21:30:00 119
2019-01-16 21:45:00 116
2019-01-16 22:00:00 117
2019-01-16 22:15:00 122
2019-01-16 22:30:00 120
2019-01-16 22:45:00 127
2019-01-16 23:00:00 137
2019-01-16 23:15:00 133
2019-01-16 23:30:00 127
2019-01-16 23:45:00 126
2019-01-17 00:00:00 130
2019-01-17 00:15:00 134
2019-01-17 00:45:00 142
2019-01-17 01:00:00 144
2019-01-17 01:30:00 146
2019-01-17 01:45:00 153
2019-01-17 20:15:00 145
2019-01-17 20:30:00 140
2019-01-17 20:45:00 140
2019-01-17 21:00:00 153
2019-01-17 21:15:00 148
2019-01-17 21:30:00 164
2019-01-17 21:45:00 195
2019-01-17 22:00:00 192
2019-01-17 22:15:00 178
2019-01-17 22:30:00 167
2019-01-19 01:30:00 147
2019-01-19 01:45:00 140
2019-01-19 02:00:00 133
2019-01-19 02:15:00 131
2019-01-19 02:30:00 129
2019-01-19 02:45:00 127
2019-01-19 03:00:00 126
2019-01-19 03:15:00 121
2019-01-19 03:30:00 117
2019-01-19 03:45:00 116
2019-01-19 04:00:00 140
2019-01-19 04:15:00 138
2019-01-19 04:30:00 132
2019-01-19 04:45:00 124
2019-01-19 05:00:00 120
2019-01-19 05:15:00 115
2019-01-19 05:30:00 114
2019-01-19 05:45:00 112
2019-01-19 22:00:00 177
2019-01-20 05:45:00 178
2019-01-20 20:15:00 218
2019-01-20 20:30:00 212
2019-01-20 20:45:00 206
2019-01-20 21:00:00 204
2019-01-20 21:15:00 199
2019-01-20 21:30:00 227
2019-01-20 21:45:00 165
2019-01-20 22:00:00 156
2019-01-20 22:15:00 152
2019-01-20 22:30:00 148
2019-01-20 22:45:00 141
2019-01-20 23:00:00 140
2019-01-20 23:15:00 158
2019-01-20 23:30:00 161
2019-01-20 23:45:00 159
2019-01-21 20:00:00 166
2019-01-21 20:15:00 158
2019-01-21 20:30:00 159
2019-01-21 20:45:00 167
2019-01-21 21:00:00 175
2019-01-21 21:15:00 184
2019-01-21 21:30:00 196
2019-01-21 21:45:00 200
2019-01-21 22:00:00 175
2019-01-21 22:15:00 172
2019-01-21 22:30:00 158
2019-01-21 22:45:00 142
2019-01-21 23:00:00 126
2019-01-22 22:15:00 198
2019-01-22 22:30:00 195
2019-01-22 22:45:00 196
2019-01-22 23:00:00 209
2019-01-22 23:15:00 156
2019-01-22 23:30:00 157
2019-01-22 23:45:00 152
2019-01-23 00:00:00 144
2019-01-23 00:15:00 135
2019-01-23 00:30:00 131
2019-01-23 00:45:00 134
2019-01-23 01:00:00 153
2019-01-23 01:15:00 156
2019-01-23 01:30:00 155
2019-01-23 01:45:00 154
2019-01-23 02:00:00 159
2019-01-23 02:15:00 165
2019-01-23 02:30:00 168
2019-01-23 02:45:00 165
2019-01-23 03:00:00 159
2019-01-23 03:15:00 153
2019-01-23 03:30:00 147
2019-01-23 03:45:00 140
2019-01-23 04:00:00 134
2019-01-23 04:15:00 130
2019-01-23 04:30:00 128
2019-01-23 04:45:00 127
2019-01-23 05:00:00 124
2019-01-23 05:15:00 118
2019-01-23 05:30:00 114
2019-01-23 05:45:00 113
2019-01-23 21:45:00 222
2019-01-23 22:15:00 198
2019-01-24 21:45:00 111
2019-01-24 22:00:00 115
2019-01-24 22:15:00 103
2019-01-25 20:00:00 152
2019-01-25 20:15:00 155
2019-01-25 20:30:00 168
2019-01-25 20:45:00 175
2019-01-25 21:00:00 174
2019-01-25 21:15:00 173
2019-01-25 21:30:00 164
2019-01-25 21:45:00 198
2019-01-25 22:00:00 216
2019-01-25 22:15:00 280
2019-01-25 22:30:00 279
2019-01-25 22:45:00 277
2019-01-25 23:00:00 279
2019-01-25 23:15:00 279
2019-01-25 23:30:00 276
2019-01-25 23:45:00 272
2019-01-26 00:00:00 271
2019-01-26 00:15:00 215
2019-01-26 00:30:00 212
2019-01-26 00:45:00 202
2019-01-26 01:00:00 195
2019-01-26 22:15:00 192
2019-01-26 22:30:00 204
2019-01-26 22:45:00 255
2019-01-26 23:00:00 241
2019-01-26 23:15:00 215
2019-01-26 23:30:00 191
2019-01-26 23:45:00 176
2019-01-27 00:00:00 156
2019-01-27 00:15:00 154
2019-01-27 00:30:00 147
2019-01-27 00:45:00 138
2019-01-27 01:00:00 134
2019-01-27 01:15:00 190
2019-01-27 01:30:00 185
2019-01-27 01:45:00 184
2019-01-27 02:00:00 175
2019-01-27 02:15:00 169
2019-01-27 02:30:00 168
2019-01-27 02:45:00 170
2019-01-27 03:00:00 164
2019-01-27 03:15:00 155
2019-01-27 03:30:00 152
2019-01-27 03:45:00 151
2019-01-27 04:00:00 148
2019-01-27 04:15:00 145
2019-01-27 04:30:00 148
2019-01-27 04:45:00 152
2019-01-27 05:00:00 154
2019-01-27 05:15:00 156
2019-01-27 05:30:00 157
2019-01-27 05:45:00 157
2019-01-27 22:45:00 251
2019-01-27 23:00:00 219
2019-01-27 23:15:00 207
2019-01-27 23:30:00 194
2019-01-27 23:45:00 176
2019-01-28 00:00:00 162
2019-01-28 00:15:00 150
2019-01-28 02:00:00 103
2019-01-28 02:15:00 101
2019-01-28 02:30:00 98
2019-01-28 02:45:00 96
2019-01-28 03:00:00 93
2019-01-28 03:15:00 88
2019-01-28 03:30:00 84
2019-01-28 03:45:00 82
2019-01-28 04:00:00 84
2019-01-28 04:15:00 86
2019-01-28 04:30:00 85
2019-01-28 04:45:00 83
2019-01-28 05:00:00 84
2019-01-28 05:15:00 83
2019-01-28 05:30:00 82
2019-01-28 05:45:00 80
2019-01-28 20:00:00 166
2019-01-28 20:15:00 166
2019-01-28 20:30:00 167
2019-01-28 20:45:00 184
2019-01-28 21:00:00 193
2019-01-28 21:15:00 193
2019-01-28 21:30:00 194
2019-01-28 21:45:00 226
2019-01-28 22:00:00 196
2019-01-28 22:15:00 193
2019-01-28 22:30:00 184
2019-01-28 22:45:00 176
2019-01-28 23:00:00 180
2019-01-28 23:15:00 208
2019-01-28 23:30:00 215
2019-01-28 23:45:00 188
2019-01-29 00:00:00 187
2019-01-29 00:15:00 189
2019-01-29 00:30:00 191
2019-01-29 00:45:00 190
2019-01-29 01:00:00 187
2019-01-29 01:15:00 184
2019-01-29 01:30:00 181
2019-01-29 01:45:00 169
2019-01-29 02:00:00 165
2019-01-29 02:15:00 162
2019-01-29 02:30:00 169
2019-01-29 02:45:00 178
2019-01-29 03:00:00 175
2019-01-29 03:15:00 170
2019-01-29 03:30:00 168
2019-01-29 03:45:00 166
2019-01-29 04:00:00 163
2019-01-29 04:15:00 159
2019-01-29 04:30:00 156
2019-01-29 04:45:00 156
2019-01-29 05:00:00 155
2019-01-29 05:30:00 143
2019-01-30 21:30:00 139
2019-01-30 21:45:00 140
2019-01-30 22:00:00 144
2019-01-30 22:15:00 138
2019-01-30 22:30:00 137
2019-01-30 22:45:00 155
2019-01-30 23:00:00 159
2019-01-30 23:15:00 178
2019-01-30 23:30:00 177
2019-01-30 23:45:00 174
2019-01-31 00:00:00 178
2019-01-31 00:15:00 185
2019-01-31 00:30:00 181
2019-01-31 00:45:00 178
2019-01-31 01:00:00 181
2019-01-31 01:15:00 184
2019-01-31 01:30:00 186
2019-01-31 01:45:00 185
2019-01-31 02:00:00 178
2019-01-31 02:15:00 166
2019-01-31 02:30:00 166
2019-01-31 02:45:00 160
2019-01-31 03:00:00 150
2019-01-31 03:15:00 161
2019-01-31 03:30:00 162
2019-01-31 03:45:00 156
2019-01-31 04:00:00 150
2019-01-31 04:15:00 146
2019-01-31 04:30:00 142
2019-01-31 04:45:00 136
2019-01-31 05:00:00 131
2019-01-31 05:15:00 128
2019-01-31 05:30:00 126
2019-01-31 05:45:00 124
2019-01-31 21:00:00 165
2019-01-31 21:15:00 168
2019-01-31 21:30:00 168
2019-01-31 21:45:00 172
2019-01-31 22:00:00 176
2019-01-31 22:15:00 167
2019-01-31 22:30:00 154
2019-01-31 22:45:00 146
2019-01-31 23:00:00 145
2019-01-31 23:15:00 144
2019-01-31 23:30:00 141
2019-01-31 23:45:00 145

Las horas de cada día del mes en las que ha estado en tiempo en rango, junto a su porcentaje de tiempo en rango del paciente 22. (1/2019)¶

In [51]:
import pandas as pd

# Configurar la opción display.max_columns y display.max_colwidth para mostrar todas las columnas y el texto completo en las celdas
pd.set_option('display.max_columns', None)
pd.set_option('display.max_colwidth', None)

# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df[(df['Patient_ID'] == 22) & (df['Measurement_date'].dt.year == 2019) & (df['Measurement_date'].dt.month == 1)]

# Filtrar solo las mediciones que están en rango (entre 70 y 180)
in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement'] >= 70) & (df_paciente_22_1_2019['Measurement'] <= 180)]

# Agrupar los datos por día y extraer las horas en las que se estuvo en rango
daily_hours_in_range = in_range.groupby(in_range['Measurement_date'].dt.date)['Measurement_time'].apply(lambda x: x.dt.hour.unique())

# Agrupar los datos por día
daily_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.date)

# Calcular el porcentaje de tiempo en rango para cada día
daily_percentage_in_range = daily_data.apply(lambda x: (x['Measurement'].between(70, 180)).mean() * 100)

# Crear un nuevo DataFrame con los valores de las horas en rango y el porcentaje de tiempo en rango para cada día
df_resultados = pd.DataFrame({'Día': daily_data.groups.keys(),
                              'Horas en rango': [f'[{", ".join(map(str, daily_hours_in_range.get(day, [])))}]' for day in daily_data.groups.keys()],
                              '% tiempo en rango': [f'{daily_percentage_in_range.get(day, 0):.2f}' for day in daily_data.groups.keys()]})

# Mostrar el resultado con los valores de la columna '% tiempo en rango' centrados
df_resultados.style.set_properties(subset=['% tiempo en rango'], **{'text-align': 'center'})
Out[51]:
  Día Horas en rango % tiempo en rango
0 2019-01-01 [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] 73.33
1 2019-01-02 [0, 1, 2, 3, 7, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] 74.67
2 2019-01-03 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] 100.00
3 2019-01-04 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 19, 20] 66.67
4 2019-01-05 [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] 72.92
5 2019-01-06 [2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 21, 22, 23] 53.95
6 2019-01-07 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23] 90.24
7 2019-01-08 [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23] 87.80
8 2019-01-09 [0, 1, 2, 3, 4, 5, 6, 11, 14, 16, 17, 18, 21, 22, 23] 45.00
9 2019-01-10 [0, 2, 3, 11, 12, 13, 14, 23] 91.67
10 2019-01-11 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 18, 20, 21] 83.87
11 2019-01-12 [0, 1, 9, 10, 11, 12, 13, 14, 15, 17, 18, 23] 72.00
12 2019-01-13 [0, 1, 8, 9, 10, 11, 12, 13, 14, 18, 19, 20, 21, 22, 23] 67.21
13 2019-01-15 [6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23] 100.00
14 2019-01-16 [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] 96.00
15 2019-01-17 [0, 1, 20, 21, 22] 87.50
16 2019-01-19 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22] 100.00
17 2019-01-20 [5, 6, 8, 9, 10, 11, 12, 13, 14, 16, 17, 21, 22, 23] 63.64
18 2019-01-21 [19, 20, 21, 22, 23] 47.83
19 2019-01-22 [23] 42.86
20 2019-01-23 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17] 88.73
21 2019-01-24 [21, 22] 100.00
22 2019-01-25 [11, 12, 15, 16, 17, 18, 19, 20, 21] 55.77
23 2019-01-26 [23] 8.33
24 2019-01-27 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 23] 75.38
25 2019-01-28 [0, 2, 3, 4, 5, 6, 7, 14, 15, 16, 17, 18, 19, 20, 22, 23] 81.97
26 2019-01-29 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 80.56
27 2019-01-30 [21, 22, 23] 100.00
28 2019-01-31 [0, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 19, 21, 22, 23] 83.33

Extrae el porcentaje de tiempo en rango de la mañana, tarde y noche del paciente 22 (1/2019)¶

In [52]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Calcular el porcentaje de tiempo en rango para la mañana (entre las 6:00 am y las 12:00 pm)
morning_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 6) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 12)]
morning_percentage_in_range = (morning_in_range['Measurement'].between(70, 180)).mean() * 100

# Calcular el porcentaje de tiempo en rango para la tarde (entre las 12:00 pm y las 8:00 pm)
afternoon_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 12) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 20)]
afternoon_percentage_in_range = (afternoon_in_range['Measurement'].between(70, 180)).mean() * 100

# Calcular el porcentaje de tiempo en rango para la noche (entre las 8:00 pm y las 6:00 am del día siguiente)
night_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 20) | (df_paciente_22_1_2019['Measurement_time'].dt.hour < 6)]
night_percentage_in_range = (night_in_range['Measurement'].between(70, 180)).mean() * 100

# Crear un nuevo DataFrame con los resultados
df_resultados = pd.DataFrame({'Período del día': ['Mañana', 'Tarde', 'Noche'],
                              '% tiempo en rango': [f'{morning_percentage_in_range:.2f}%',
                                                    f'{afternoon_percentage_in_range:.2f}%',
                                                    f'{night_percentage_in_range:.2f}%']})
# Mostrar el resultado
display(df_resultados)
Período del día % tiempo en rango
0 Mañana 88.17%
1 Tarde 71.01%
2 Noche 74.86%

Gráfico de barras del tiempo en rango de la mañana, tarde y noche del paciente 83 (10/2021)¶

In [53]:
import matplotlib.pyplot as plt

# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df[(df['Patient_ID'] == 22) & (df['Measurement_date'].dt.year == 2019) & (df['Measurement_date'].dt.month == 1)]

# Calcular el porcentaje de tiempo en rango para la mañana (entre las 6:00 am y las 12:00 pm)
morning_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 6) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 12)]
morning_percentage_in_range = (morning_in_range['Measurement'].between(70, 180)).mean() * 100

# Calcular el porcentaje de tiempo en rango para la tarde (entre las 12:00 pm y las 8:00 pm)
afternoon_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 12) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 20)]
afternoon_percentage_in_range = (afternoon_in_range['Measurement'].between(70, 180)).mean() * 100

# Calcular el porcentaje de tiempo en rango para la noche (entre las 8:00 pm y las 6:00 am del día siguiente)
night_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 20) | (df_paciente_22_1_2019['Measurement_time'].dt.hour < 6)]
night_percentage_in_range = (night_in_range['Measurement'].between(70, 180)).mean() * 100

# Crear un nuevo DataFrame con los porcentajes de tiempo en rango para la mañana, tarde y noche como columnas
result = pd.DataFrame({'Mañana': [morning_percentage_in_range], 'Tarde': [afternoon_percentage_in_range], 'Noche': [night_percentage_in_range]})

# Crear un gráfico de barras mostrando los porcentajes de tiempo en rango para la mañana, tarde y noche
ax = result.plot(kind='bar', rot=0, color=['#0e4d82', '#bb3f11', '#7d6c5c'])

# Establecer las etiquetas de los ejes X e Y
ax.set_xlabel('Hora del día')
ax.set_ylabel('% Tiempo en rango')

# Añadir título al gráfico
plt.title('Porcentaje de tiempo en rango por hora del día')

# Establecer el rango del eje Y
plt.ylim([0, 100])

# Mostrar los valores exactos de los porcentajes dentro de las barras
for p in ax.patches:
    ax.annotate(f'{p.get_height():.2f}%', (p.get_x() + p.get_width() / 2., p.get_height() / 2), ha='center', va='center', color='white', fontweight='bold')

plt.show()

Extraer la media de la glucosa para cada hora de todos los día del mes del paciente 22 (1/2019)¶

In [54]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df[(df['Patient_ID'] == 22) & (df['Measurement_date'].dt.year == 2019) & (df['Measurement_date'].dt.month == 1)]

# Agrupar los datos por hora
hourly_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_time'].dt.hour)

# Calcular la media de la columna Measurement para cada hora
hourly_mean = hourly_data['Measurement'].mean()

# Crear un nuevo DataFrame con los resultados
df_resultados = pd.DataFrame({'Hora': hourly_mean.index, 'Media': hourly_mean.values})

# Formatear la columna Media para mostrar solo dos decimales
df_resultados['Media'] = df_resultados['Media'].apply(lambda x: '{:.2f}'.format(x))

# Mostrar el resultado en forma de tabla de DataFrame sin índice
df_resultados.style.hide(axis='index')
Out[54]:
Hora Media
0 165.51
1 160.29
2 156.67
3 154.38
4 148.72
5 143.32
6 139.92
7 144.65
8 151.18
9 147.76
10 151.13
11 156.65
12 167.71
13 159.31
14 148.80
15 169.62
16 169.23
17 158.60
18 162.03
19 170.74
20 165.62
21 161.85
22 165.24
23 169.26

Extraer la hora (3 mayores porcentajes) con el mayor porcentaje en tiempo en rango y su porcentaje de tiempo en rango del paciente 22. (1/2019)¶

También extrae el porcentaje de tiempo en rango de cada hora

In [55]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Agrupar los datos por hora
hourly_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_time'].dt.hour)

# Calcular el porcentaje de tiempo en rango para cada hora
hourly_percentage_in_range = hourly_data.apply(lambda x: (x['Measurement'].between(70, 180)).mean() * 100)

# Encontrar la hora o las horas con el mayor porcentaje de tiempo en rango
max_percentage = hourly_percentage_in_range.max()
best_hours = hourly_percentage_in_range[hourly_percentage_in_range == max_percentage].index
best_hours_str = ', '.join(map(str, best_hours.tolist()))

# Crear un DataFrame con la hora o las horas con el mayor porcentaje de tiempo en rango
df_best_hours = pd.DataFrame({'Hora': [best_hours_str], 'Porcentaje de tiempo en rango': [f'{max_percentage:.2f}%']})

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('La hora con el mayor porcentaje de tiempo en rango es:')
display(df_best_hours.style.hide(axis='index'))

top_3 = hourly_percentage_in_range.nlargest(3)

# Crear un DataFrame con las 3 mayores porcentajes de tiempo en rango
df_top_3 = pd.DataFrame({'Hora': top_3.index, 'Porcentaje de tiempo en rango': top_3.apply(lambda x: f'{x:.2f}%')})

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nLas 3 mayores porcentajes de tiempo en rango son:')
display(df_top_3.style.hide(axis='index'))

# Crear un DataFrame con todos los porcentajes de tiempo en rango
df_all_percentages = pd.DataFrame({'Hora': hourly_percentage_in_range.index, 'Porcentaje de tiempo en rango': hourly_percentage_in_range.apply(lambda x: f'{x:.2f}%')})

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nTodos los porcentajes de tiempo en rango son:')
display(df_all_percentages.style.hide(axis='index'))
La hora con el mayor porcentaje de tiempo en rango es:
Hora Porcentaje de tiempo en rango
4 93.10%
Las 3 mayores porcentajes de tiempo en rango son:
Hora Porcentaje de tiempo en rango
4 93.10%
5 92.98%
9 91.38%
Todos los porcentajes de tiempo en rango son:
Hora Porcentaje de tiempo en rango
0 62.69%
1 61.29%
2 87.30%
3 80.88%
4 93.10%
5 92.98%
6 88.71%
7 88.71%
8 89.09%
9 91.38%
10 88.52%
11 82.46%
12 66.67%
13 80.00%
14 84.06%
15 70.69%
16 66.67%
17 65.08%
18 68.12%
19 64.52%
20 75.00%
21 66.67%
22 69.32%
23 68.97%

Las horas de cada día del mes en las que ha estado en Hipoglucemia (<70) o Hiperglucemia (>180), junto a su porcentaje de Hipoglucemia (<70) o Hiperglucemia (>180) del paciente 22. (1/2019)¶

In [56]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Filtrar solo las mediciones que están fuera de rango (menor a 70 o mayor a 180)
out_of_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement'] < 70) | (df_paciente_22_1_2019['Measurement'] > 180)]

# Agrupar los datos por día y extraer las horas en las que se estuvo fuera de rango
daily_hours_out_of_range = out_of_range.groupby(out_of_range['Measurement_date'].dt.date)['Measurement_time'].apply(lambda x: x.dt.hour.unique())

# Agrupar los datos por día
daily_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.date)

# Calcular el porcentaje de tiempo en hipoglucemia (<70) y hiperglucemia (>180) para cada día
daily_percentage_hypo = daily_data.apply(lambda x: (x['Measurement'] < 70).mean() * 100)
daily_percentage_hyper = daily_data.apply(lambda x: (x['Measurement'] > 180).mean() * 100)

# Crear un DataFrame con los resultados
data = []
for day in daily_data.groups.keys():
    hours = daily_hours_out_of_range.get(day, [])
    hours_str = f'[{", ".join(map(str, hours))}]'
    percentage_hypo = daily_percentage_hypo.get(day, 0)
    percentage_hyper = daily_percentage_hyper.get(day, 0)
    data.append({'Día': str(day), 'Horas fuera de rango': hours_str, '% tiempo en hipoglucemia': f'{percentage_hypo:.2f}%', '% tiempo en hiperglucemia': f'{percentage_hyper:.2f}%'})
df_resultados = pd.DataFrame(data)

# Mostrar el resultado en forma de tabla de DataFrame sin índice
display(df_resultados.style.hide(axis='index'))
Día Horas fuera de rango % tiempo en hipoglucemia % tiempo en hiperglucemia
2019-01-01 [0, 1, 2, 3] 0.00% 26.67%
2019-01-02 [3, 4, 5, 6, 17, 18] 0.00% 25.33%
2019-01-03 [] 0.00% 0.00%
2019-01-04 [2, 3, 16, 17, 18, 19, 20, 21, 22, 23] 0.00% 33.33%
2019-01-05 [0, 1, 19, 20, 21, 22, 23] 0.00% 27.08%
2019-01-06 [0, 1, 2, 15, 16, 17, 18, 19, 20, 21] 0.00% 46.05%
2019-01-07 [10, 11, 18, 19] 0.00% 9.76%
2019-01-08 [3, 12, 15, 16] 0.00% 12.20%
2019-01-09 [1, 2, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 19, 20, 21] 0.00% 55.00%
2019-01-10 [12, 14] 0.00% 8.33%
2019-01-11 [11, 21, 22, 23] 0.00% 16.13%
2019-01-12 [0, 12, 16, 17, 19] 0.00% 28.00%
2019-01-13 [10, 14, 15, 16, 17, 18, 23] 0.00% 32.79%
2019-01-15 [] 0.00% 0.00%
2019-01-16 [13] 0.00% 4.00%
2019-01-17 [21, 22] 0.00% 12.50%
2019-01-19 [] 0.00% 0.00%
2019-01-20 [6, 7, 11, 17, 18, 19, 20, 21] 0.00% 36.36%
2019-01-21 [17, 18, 19, 21] 0.00% 52.17%
2019-01-22 [22, 23] 0.00% 57.14%
2019-01-23 [11, 12, 13, 21, 22] 0.00% 11.27%
2019-01-24 [] 0.00% 0.00%
2019-01-25 [12, 13, 14, 15, 21, 22, 23] 0.00% 44.23%
2019-01-26 [0, 1, 22, 23] 0.00% 91.67%
2019-01-27 [1, 11, 12, 13, 22, 23] 0.00% 24.62%
2019-01-28 [20, 21, 22, 23] 0.00% 18.03%
2019-01-29 [0, 1] 0.00% 19.44%
2019-01-30 [] 0.00% 0.00%
2019-01-31 [0, 1, 8, 9, 16] 0.00% 16.67%

Extrae el porcentaje de tiempo en en Hipoglucemia (<70) y Hiperglucemia (>180) de la mañana, tarde y noche del paciente 22. (1/2019)¶

In [57]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Calcular el porcentaje de tiempo en hipoglucemia (<70) y hiperglucemia (>180) para la mañana (entre las 6:00 am y las 12:00 pm)
morning_data = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 6) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 12)]
morning_percentage_hypo = (morning_data['Measurement'] < 70).mean() * 100
morning_percentage_hyper = (morning_data['Measurement'] > 180).mean() * 100

# Calcular el porcentaje de tiempo en hipoglucemia (<70) y hiperglucemia (>180) para la tarde (entre las 12:00 pm y las 8:00 pm)
afternoon_data = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 12) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 20)]
afternoon_percentage_hypo = (afternoon_data['Measurement'] < 70).mean() * 100
afternoon_percentage_hyper = (afternoon_data['Measurement'] > 180).mean() * 100

# Calcular el porcentaje de tiempo en hipoglucemia (<70) y hiperglucemia (>180) para la noche (entre las 8:00 pm y las 6:00 am del día siguiente)
night_data = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 20) | (df_paciente_22_1_2019['Measurement_time'].dt.hour < 6)]
night_percentage_hypo = (night_data['Measurement'] < 70).mean() * 100
night_percentage_hyper = (night_data['Measurement'] > 180).mean() * 100

# Crear un DataFrame con los resultados
data = [{'Parte del día': 'Mañana', '% tiempo en hipoglucemia': f'{morning_percentage_hypo:.2f}%', '% tiempo en hiperglucemia': f'{morning_percentage_hyper:.2f}%'},
        {'Parte del día': 'Tarde', '% tiempo en hipoglucemia': f'{afternoon_percentage_hypo:.2f}%', '% tiempo en hiperglucemia': f'{afternoon_percentage_hyper:.2f}%'},
        {'Parte del día': 'Noche', '% tiempo en hipoglucemia': f'{night_percentage_hypo:.2f}%', '% tiempo en hiperglucemia': f'{night_percentage_hyper:.2f}%'}]
df_resultados = pd.DataFrame(data)

# Mostrar el resultado en forma de tabla de DataFrame sin índice
display(df_resultados.style.hide(axis='index'))
Parte del día % tiempo en hipoglucemia % tiempo en hiperglucemia
Mañana 0.00% 11.83%
Tarde 0.00% 28.99%
Noche 0.00% 25.14%

Extraer la hora (3 mayores porcentajes) con el mayor porcentaje en Hipoglucemia (<70) y Hiperglucemia (>180) y su porcentaje de Hipoglucemia (<70) o Hiperglucemia (>180) del paciente 22. (1/2019)¶

También extrae el porcentaje de tiempo en rango de cada hora

In [58]:
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Agrupar los datos por hora
hourly_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_time'].dt.hour)

# Calcular el porcentaje de tiempo en hipoglucemia (<70) e hiperglucemia (>180) para cada hora
hourly_percentage_hypo = hourly_data.apply(lambda x: (x['Measurement'] < 70).mean() * 100)
hourly_percentage_hyper = hourly_data.apply(lambda x: (x['Measurement'] > 180).mean() * 100)

# Encontrar la hora o las horas con el mayor porcentaje de tiempo en hipoglucemia
max_percentage_hypo = hourly_percentage_hypo.max()
best_hours_hypo = hourly_percentage_hypo[hourly_percentage_hypo == max_percentage_hypo].index
best_hours_str_hypo = ', '.join(map(str, best_hours_hypo.tolist()))

# Encontrar la hora o las horas con el mayor porcentaje de tiempo en hiperglucemia
max_percentage_hyper = hourly_percentage_hyper.max()
best_hours_hyper = hourly_percentage_hyper[hourly_percentage_hyper == max_percentage_hyper].index
best_hours_str_hyper = ', '.join(map(str, best_hours_hyper.tolist()))

# Crear un DataFrame con la hora o las horas con el mayor porcentaje de tiempo en hipoglucemia e hiperglucemia
data = [{'Hora': best_hours_str_hypo, 'Porcentaje de tiempo': f'{max_percentage_hypo:.2f}%', 'Tipo': 'Hipoglucemia'},
        {'Hora': best_hours_str_hyper, 'Porcentaje de tiempo': f'{max_percentage_hyper:.2f}%', 'Tipo': 'Hiperglucemia'}]
df_best_hours = pd.DataFrame(data)

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('La hora con el mayor porcentaje de tiempo en hipoglucemia e hiperglucemia es:')
display(df_best_hours.style.hide(axis='index'))

# Encontrar las 3 horas con los mayores porcentajes de tiempo en hipoglucemia
top_3_hypo = hourly_percentage_hypo.nlargest(3)

# Crear un DataFrame con las 3 horas con los mayores porcentajes de tiempo en hipoglucemia
data = [{'Hora': hour, 'Porcentaje de tiempo': f'{percentage:.2f}%'} for hour, percentage in top_3_hypo.items()]
df_top_3_hypo = pd.DataFrame(data)

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nLas 3 mayores porcentajes de tiempo en hipoglucemia son:')
display(df_top_3_hypo.style.hide(axis='index'))

# Encontrar las 3 horas con los mayores porcentajes de tiempo en hiperglucemia
top_3_hyper = hourly_percentage_hyper.nlargest(3)

# Crear un DataFrame con las 3 horas con los mayores porcentajes de tiempo en hiperglucemia
data = [{'Hora': hour, 'Porcentaje de tiempo': f'{percentage:.2f}%'} for hour, percentage in top_3_hyper.items()]
df_top_3_hyper = pd.DataFrame(data)

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nLas 3 mayores porcentajes de tiempo en hiperglucemia son:')
display(df_top_3_hyper.style.hide(axis='index'))

# Crear un DataFrame con todos los porcentajes de tiempo en hipoglucemia e hiperglucemia para cada hora
data = [{'Hora': hour, '% tiempo en hipoglucemia': f'{hourly_percentage_hypo.get(hour, 0):.2f}%', '% tiempo en hiperglucemia': f'{hourly_percentage_hyper.get(hour, 0):.2f}%'} for hour in range(24)]
df_all_percentages = pd.DataFrame(data)

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nTodos los porcentajes de tiempo en hipoglucemia e hiperglucemia para cada hora son:')
display(df_all_percentages.style.hide(axis='index'))
La hora con el mayor porcentaje de tiempo en hipoglucemia e hiperglucemia es:
Hora Porcentaje de tiempo Tipo
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 0.00% Hipoglucemia
1 38.71% Hiperglucemia
Las 3 mayores porcentajes de tiempo en hipoglucemia son:
Hora Porcentaje de tiempo
0 0.00%
1 0.00%
2 0.00%
Las 3 mayores porcentajes de tiempo en hiperglucemia son:
Hora Porcentaje de tiempo
1 38.71%
0 37.31%
19 35.48%
Todos los porcentajes de tiempo en hipoglucemia e hiperglucemia para cada hora son:
Hora % tiempo en hipoglucemia % tiempo en hiperglucemia
0 0.00% 37.31%
1 0.00% 38.71%
2 0.00% 12.70%
3 0.00% 19.12%
4 0.00% 6.90%
5 0.00% 7.02%
6 0.00% 11.29%
7 0.00% 11.29%
8 0.00% 10.91%
9 0.00% 8.62%
10 0.00% 11.48%
11 0.00% 17.54%
12 0.00% 33.33%
13 0.00% 20.00%
14 0.00% 15.94%
15 0.00% 29.31%
16 0.00% 33.33%
17 0.00% 34.92%
18 0.00% 31.88%
19 0.00% 35.48%
20 0.00% 25.00%
21 0.00% 33.33%
22 0.00% 30.68%
23 0.00% 31.03%

Muestra si hay mediciones de datos en todos los meses del año del paciente 22¶

In [59]:
df_paciente_22_2019 = df_paciente_22_2019[(df_paciente_22_2019['Patient_ID'] == 22) & (df_paciente_22_2019['Measurement_date'].dt.year == 2019)]
meses = df_paciente_22_2019['Measurement_date'].dt.month.unique()

# Crear un DataFrame con los meses para los que hay datos
df_meses = pd.DataFrame({'Meses': meses})

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('Hay datos para los siguientes meses del año 2019 para el paciente 22:')
display(df_meses.style.hide(axis='index'))

if len(meses) == 12:
    print('Hay datos para todos los meses del año 2019 para el paciente 22')
else:
    print('Faltan datos para algunos meses del año 2019 para el paciente 22')
Hay datos para los siguientes meses del año 2019 para el paciente 22:
Meses
1
2
3
4
5
6
8
9
10
11
12
Faltan datos para algunos meses del año 2019 para el paciente 22
In [60]:
# Crear una lista de todos los meses en un año
todos_meses = list(range(1,13))

df_paciente_22_2019 = df_paciente_22_2019[(df_paciente_22_2019['Patient_ID'] == 22) & (df_paciente_22_2019['Measurement_date'].dt.year == 2019)]
meses_con_datos = df_paciente_22_2019['Measurement_date'].dt.month.unique()

# Encontrar los meses que no están en la lista de meses con datos
meses_sin_datos = [mes for mes in todos_meses if mes not in meses_con_datos]

# Crear un DataFrame con los meses para los que hay datos y los que no
df_meses_con_datos = pd.DataFrame({'Meses con datos': meses_con_datos})
df_meses_sin_datos = pd.DataFrame({'Meses sin datos': meses_sin_datos})

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('Para el paciente 22 en el año 2019:')
display(df_meses_con_datos.style.hide(axis='index'))
display(df_meses_sin_datos.style.hide(axis='index'))

if len(meses_con_datos) == 12:
    print('Hay datos para todos los meses.')
else:
    print('Faltan datos para algunos meses.')
Para el paciente 22 en el año 2019:
Meses con datos
1
2
3
4
5
6
8
9
10
11
12
Meses sin datos
7
Faltan datos para algunos meses.

Mostrar la media de la hemoglobina glicosilada HbA1c de todos los meses del año 2019 del paciente 22¶

In [61]:
df_filtered = df_paciente_22_2019[(df_paciente_22_2019['Patient_ID'] == 22) & (df_paciente_22_2019['Measurement_date'].dt.year == 2019)]

# Crear un DataFrame con la media de la hemoglobina glicosilada HbA1c para cada mes
data = []
for month in df_filtered['Measurement_date'].dt.month.unique():
    df_month = df_filtered[df_filtered['Measurement_date'].dt.month == month]
    eAG = df_month['Measurement'].mean()
    A1c = (eAG + 46.7) / 28.7
    data.append({'Mes': month, 'A1c promedio': f'{A1c:.2f}%'})
df_resultados = pd.DataFrame(data)

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('La media de la hemoglobina glicosilada HbA1c de todos los meses del año 2019 del paciente 22 es:')
display(df_resultados.style.hide(axis='index'))
La media de la hemoglobina glicosilada HbA1c de todos los meses del año 2019 del paciente 22 es:
Mes A1c promedio
1 7.14%
2 7.13%
3 7.27%
4 7.54%
5 7.01%
6 7.29%
8 7.19%
9 7.24%
10 6.89%
11 6.89%
12 7.29%

Gráfico de barras de la hemoglobina glicosilada HbA1c de todos los meses del año 2021 del paciente 83¶

In [62]:
import matplotlib.pyplot as plt

# Filtrar los datos para el paciente 22 en el año 2019
df_filtered = df_paciente_22_2019[(df_paciente_22_2019['Patient_ID'] == 22) & (df_paciente_22_2019['Measurement_date'].dt.year == 2019)]

# Calcular la media de la hemoglobina glicosilada HbA1c para cada mes
months = []
a1c_values = []
for month in df_filtered['Measurement_date'].dt.month.unique():
    df_month = df_filtered[df_filtered['Measurement_date'].dt.month == month]
    eAG = df_month['Measurement'].mean()
    A1c = (eAG + 46.7) / 28.7
    months.append(month)
    a1c_values.append(A1c)

# Crear un nuevo DataFrame con los meses y los valores de HbA1c como columnas
result = pd.DataFrame({'Month': months, 'HbA1c': a1c_values})

# Crear un gráfico de barras o de líneas mostrando la media de la hemoglobina glicosilada HbA1c para cada mes
ax = result.plot.bar(x='Month', y='HbA1c', rot=0)
# ax = result.plot(x='Month', y='HbA1c')

# Establecer las etiquetas de los ejes X e Y
ax.set_xlabel('Month')
ax.set_ylabel('HbA1c (%)')

plt.show()

Mostrar la hemoglobina glicosilada HbA1c de todos los dias del mes 1 del año 2019 del paciente 22¶

In [63]:
df_filtered = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Crear un DataFrame con la hemoglobina glicosilada HbA1c para cada día
data = []
for day in df_filtered['Measurement_date'].dt.day.unique():
    df_day = df_filtered[df_filtered['Measurement_date'].dt.day == day]
    eAG = df_day['Measurement'].mean()
    A1c = (eAG + 46.7) / 28.7
    data.append({'Día': day, 'A1c': f'{A1c:.2f}%'})
df_resultados = pd.DataFrame(data)

# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('La hemoglobina glicosilada HbA1c de todos los días del mes 1, del año 2019, del paciente 22 es:')
display(df_resultados.style.hide(axis='index'))
La hemoglobina glicosilada HbA1c de todos los días del mes 1, del año 2019, del paciente 22 es:
Día A1c
1 7.08%
2 7.00%
3 6.80%
4 7.53%
5 7.34%
6 7.87%
7 6.76%
8 6.96%
9 7.79%
10 7.41%
11 7.52%
12 7.04%
13 7.39%
15 5.96%
16 6.47%
17 7.01%
19 5.97%
20 7.68%
21 7.79%
22 7.91%
23 6.91%
24 5.45%
25 7.73%
26 9.09%
27 7.46%
28 6.29%
29 7.05%
30 7.00%
31 7.03%

Gráfico de barras de la hemoglobina glicosilada HbA1c de todos los dias del mes 1 del año 2019 del paciente 22¶

In [64]:
from matplotlib.figure import Figure
from IPython.display import display

# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_filtered = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]

# Calcular la hemoglobina glicosilada HbA1c para cada día
days = []
a1c_values = []
for day in df_filtered['Measurement_date'].dt.day.unique():
    df_day = df_filtered[df_filtered['Measurement_date'].dt.day == day]
    eAG = df_day['Measurement'].mean()
    A1c = (eAG + 46.7) / 28.7
    days.append(day)
    a1c_values.append(A1c)

# Crear un nuevo DataFrame con los días y los valores de HbA1c como columnas
result = pd.DataFrame({'Day': days, 'HbA1c': a1c_values})

# Crear una instancia de la clase Figure y establecer su tamaño
fig = Figure()
fig.set_size_inches(5, 5)

# Crear un objeto Axes a partir de la figura
ax = fig.subplots()

# Utilizar el objeto Axes para crear el gráfico de barras
ax.bar(result['Day'], result['HbA1c'])
ax.set_xlabel('Day')
ax.set_ylabel('HbA1c (%)')

# Mostrar la figura
display(fig)

Tabla de hemoglobina glicosilada HbA1c para comprobar la relación que hay entre los niveles de glucosa, la hemoglobina glicosilada y el riesgo de generar complicaciones¶

Tabla de la hemoglobina glicosilada HbA1c

Clarke Error Grid¶

El Clarke Error Grid permite determinar la calidad y precisión del modelo ya que evalúa y cuantifica el riesgo asociado con las estimaciones de glucosa en sangre en comparación con los valores precisos conocidos. Ayuda a determinar qué tan cerca o lejos están las estimaciones de los valores reales y clasificar las estimaciones en diferentes zonas de riesgo. Las zonas de riesgo son A, B, C, D y E:

  • Zona A: Resultados médicamente precisos y cercanos a los valores conocidos. Bajo riesgo y deseable. Valores que están dentro del 20% del valor del sensor de referencia. Estos valores se consideran clínicamente precisos. Por ejemplo, si el valor de referencia es de 100 mg/dL, un valor medido entre 80 y 120 mg/dL estaría en la Zona A. En este caso, el medidor de glucosa proporciona una medición precisa y no se espera que lleve a un tratamiento inapropiado.
  • Zona B: Resultados médicamente aceptables, aunque con una desviación mayor que la zona A. Todavía útiles, pero pueden requerir ajustes. Valores que están fuera del 20% pero que no llevarían a un tratamiento inapropiado. Por ejemplo, si el valor de referencia es de 200 mg/dL, un valor medido de 160 mg/dL estaría en la Zona B. Aunque el valor medido no es completamente preciso, no se espera que lleve a un tratamiento inapropiado.
  • Zona C: Tratamiento innecesario. Estimaciones dentro del rango aceptable, pero alejadas de los valores conocidos. Valores que llevarían a un tratamiento innecesario. Por ejemplo, si el valor de referencia es de 100 mg/dL y el medidor de glucosa indica un nivel alto de glucosa en sangre de 250 mg/dL, el paciente podría recibir tratamiento para reducir su nivel de glucosa en sangre cuando en realidad no lo necesita. Esto podría tener consecuencias negativas para la salud del paciente.
  • Zona D: Incapacidad para detectar una condición peligrosa. Estimaciones fuera del rango aceptable, sin reflejar adecuadamente los niveles reales. Falla potencialmente peligrosa para detectar hipoglucemia o hiperglucemia. Por ejemplo, si el valor de referencia es de 50 mg/dL, lo que indica hipoglucemia, y el medidor de glucosa indica un nivel normal de glucosa en sangre de 100 mg/dL, el paciente podría no recibir tratamiento para aumentar su nivel de glucosa en sangre cuando en realidad lo necesita. Esto podría ser peligroso para la salud del paciente.
  • Zona E: Confusión entre hipoglucemia grave e hiperglucemia. Alto riesgo y crítico. Valores que confundirían el tratamiento de la hipoglucemia con la hiperglucemia y viceversa. Por ejemplo, si el valor de referencia es de 50 mg/dL, lo que indica hipoglucemia, y el medidor de glucosa indica un nivel alto de glucosa en sangre de 250 mg/dL, el paciente podría recibir tratamiento para reducir su nivel de glucosa en sangre cuando en realidad necesita aumentarlo. Esto podría tener consecuencias graves para la salud del paciente.

En resumen, las zonas A y B son las más deseables, indicando resultados precisos o aceptables. La zona C implica un tratamiento innecesario, mientras que la zona D representa la incapacidad para detectar una situación peligrosa. La zona E es la más crítica, indicando la confusión entre hipoglucemia grave e hiperglucemia.

Dataframe Completo¶

In [65]:
df
Out[65]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-02-21 19:45:00 83 2018-02-21 1900-01-01 19:46:00 121 True False False 19 2
2018-02-21 20:00:00 83 2018-02-21 1900-01-01 20:01:00 124 True False False 20 2
2018-02-21 20:15:00 83 2018-02-21 1900-01-01 20:17:00 131 True False False 20 2
2018-02-21 20:30:00 83 2018-02-21 1900-01-01 20:32:00 128 True False False 20 2
2018-02-21 20:45:00 83 2018-02-21 1900-01-01 20:47:00 129 True False False 20 2
... ... ... ... ... ... ... ... ... ...
2022-03-21 19:45:00 27 2022-03-21 1900-01-01 19:48:00 264 False False True 19 0
2022-03-21 20:00:00 27 2022-03-21 1900-01-01 20:03:00 269 False False True 20 0
2022-03-21 20:15:00 27 2022-03-21 1900-01-01 20:18:00 283 False False True 20 0
2022-03-21 20:30:00 27 2022-03-21 1900-01-01 20:33:00 315 False False True 20 0
2022-03-21 20:45:00 27 2022-03-21 1900-01-01 20:48:00 327 False False True 20 0

136340 rows × 9 columns

In [66]:
df.dtypes
Out[66]:
Patient_ID                   Int32
Measurement_date    datetime64[ns]
Measurement_time    datetime64[ns]
Measurement                  Int64
In_Range                      bool
Hypoglycemia                  bool
Hyperglycemia                 bool
Hour_of_Day                  Int64
Day_of_Week                  Int64
dtype: object

DataFrame Completo Paciente 22¶

  • df_paciente_22_2019: Completo del 2019 (6099 rows × 9 columns)
  • df_paciente_22_1_2019: Completo 1/2019 (1565 rows × 9 columns)
In [67]:
df_paciente_22
Out[67]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-11-06 06:45:00 22 2018-11-06 1900-01-01 06:45:00 134 True False False 6 1
2018-11-06 07:00:00 22 2018-11-06 1900-01-01 07:00:00 134 True False False 7 1
2018-11-06 07:15:00 22 2018-11-06 1900-01-01 07:15:00 139 True False False 7 1
2018-11-06 07:30:00 22 2018-11-06 1900-01-01 07:30:00 145 True False False 7 1
2018-11-06 07:45:00 22 2018-11-06 1900-01-01 07:45:00 147 True False False 7 1
... ... ... ... ... ... ... ... ... ...
2022-03-08 11:30:00 22 2022-03-08 1900-01-01 11:30:00 179 True False False 11 1
2022-03-08 12:30:00 22 2022-03-08 1900-01-01 12:30:00 173 True False False 12 1
2022-03-08 12:45:00 22 2022-03-08 1900-01-01 12:45:00 171 True False False 12 1
2022-03-08 13:15:00 22 2022-03-08 1900-01-01 13:15:00 165 True False False 13 1
2022-03-15 23:45:00 22 2022-03-15 1900-01-01 23:46:00 155 True False False 23 1

10409 rows × 9 columns

In [68]:
df_paciente_22.dtypes
Out[68]:
Patient_ID                   Int32
Measurement_date    datetime64[ns]
Measurement_time    datetime64[ns]
Measurement                  Int64
In_Range                      bool
Hypoglycemia                  bool
Hyperglycemia                 bool
Hour_of_Day                  Int64
Day_of_Week                  Int64
dtype: object

Comparativa de soluciones con los modelos RNN (LSTM) y CRNN
¶

Se va a realizar una comparativa de soluciones con los modelos de redes neuronales recurrentes (RNN) y redes neuronales recurrentes convolucionales (CRNN), utilizando series temporales para la predicción de los niveles de glucosa en diabetes tipo 1. El rendimiento de ambos modelos se va a tratar de mejorar mediante la combinación de los distintos parámetros de los algoritmos profundos y de las distintas extracciones de características del conjunto de datos.

Mostrar los 5 pacientes con mayor número de mediciones desde el inicio hasta el final del estudio y su cantidad de mediciones totales junto su media total¶

In [69]:
# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y contar el número de mediciones ('Measurement_time') que cada paciente ha realizado.
# Luego ordenar los resultados de manera descendente ('sort_values(ascending=False)') y seleccionar los primeros 5 pacientes con más mediciones ('head(5)').
top_5_pacientes = df.groupby('Patient_ID')['Measurement_time'].count().sort_values(ascending=False).head(5)

# Crear un nuevo DataFrame 'df_top_5_pacientes_modificado' que contiene los mismos datos que 'top_5_pacientes',
# pero reseteamos el índice para que el ID de paciente y el número de mediciones estén en columnas separadas.
df_top_5_pacientes_modificado = top_5_pacientes.reset_index()

# Renombrar las columnas del DataFrame 'df_top_5_pacientes_modificado'.
df_top_5_pacientes_modificado.columns = ['Patient_ID', 'Num_Mediciones']

# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y obtener la fecha mínima ('min') y máxima ('max') de las mediciones.
# Luego resetear el índice y se guarda en el DataFrame 'fechas_min_max'.
fechas_min_max = df.groupby('Patient_ID')['Measurement_date'].agg(['min', 'max']).reset_index()

# Unir el DataFrame 'df_top_5_pacientes_modificado' y el DataFrame 'fechas_min_max' en una nueva DataFrame 'df_top_5_pacientes_modificado'
# Utilizar el ID de paciente ('Patient_ID') como clave de unión.
df_top_5_pacientes_modificado = df_top_5_pacientes_modificado.merge(fechas_min_max, on='Patient_ID')

# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y calcular la media de la columna 'Measurement' para cada paciente
media_glucosa = df.groupby('Patient_ID')['Measurement'].mean()

# Crear un nuevo DataFrame 'df_media_glucosa' que contiene los mismos datos que 'media_glucosa',
# pero reseteamos el índice para que el ID de paciente y la media de glucosa estén en columnas separadas.
df_media_glucosa = media_glucosa.reset_index()

# Renombrar las columnas del DataFrame 'df_media_glucosa'.
df_media_glucosa.columns = ['Patient_ID', 'Media_Glucosa']

# Unir el DataFrame 'df_top_5_pacientes_modificado' y el DataFrame 'df_media_glucosa' en una nueva DataFrame 'df_top_5_pacientes_modificado'
# Utilizar el ID de paciente ('Patient_ID') como clave de unión.
df_top_5_pacientes_modificado = df_top_5_pacientes_modificado.merge(df_media_glucosa, on='Patient_ID')
df_top_5_pacientes_modificado
Out[69]:
Patient_ID Num_Mediciones min max Media_Glucosa
0 24 11987 2018-07-13 2022-03-12 151.588888
1 11 11332 2018-06-12 2022-02-25 150.851747
2 22 10409 2018-11-06 2022-03-15 157.267653
3 83 9198 2018-02-21 2022-03-14 144.642314
4 92 8509 2018-06-06 2022-01-10 152.568692

Experimento con los 5 pacientes con mayor número de mediciones de glucosa¶

Paciente 22
¶

La comparativa extensa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 22 debido a que tras un análisis extenso del paciente 22 se ha detectado que es el que prácticamente tiene más mediciones y es el que tiene un mayor número de mediciones en un mes, lo cual facilita al algoritmo en descubrir patrones en los datos.

Posteriormente a la comparativa de soluciones del paciente 22, se llevará a cabo más comparativas de soluciones con los modelos RNN y CRNN pero con el resto de los 5 pacientes con más mediciones. De esta manera se podrá confirmar que el experimento se puede llevar a cabo de manera personalizada para cada uno de los pacientes.

Red Neuronal Recurrente (RNN) con el algoritmo Long Short-Term Memory (LSTM)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo LSTM con características de entrada: 'Measurement'¶

En este caso se esta entrenando al algoritmo LSTM con la variable 'Measurement'.

  • 'Mesurement' es el nivel de glucosa.

Mejor arquitectura 1¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [321]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import Dropout
from tensorflow.keras.callbacks import EarlyStopping

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

2. Dividir el conjunto de datos¶

In [322]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [323]:
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement']], seq_length)
X_val, y_val = create_sequences(val[['Measurement']], seq_length)
X_test, y_test = create_sequences(test[['Measurement']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 1))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], 1)))
model.add(Dense(1))
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_45"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_55 (LSTM)              (None, 32)                4352      
                                                                 
 dense_45 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,385
Trainable params: 4,385
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [324]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1471/1471 [==============================] - 4s 2ms/step - loss: 136.4953 - val_loss: 124.6187
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 104.0943 - val_loss: 92.3128
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 72.6040 - val_loss: 61.3360
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 42.4538 - val_loss: 30.9649
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 20.2102 - val_loss: 12.0011
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 12.3273 - val_loss: 6.8520
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 8.0267 - val_loss: 3.7079
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 5.8618 - val_loss: 2.2610
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.5002 - val_loss: 1.0836
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.6757 - val_loss: 1.1721
Epoch 11/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.2800 - val_loss: 0.9048
Epoch 12/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.1149 - val_loss: 1.7230
Epoch 13/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.1932 - val_loss: 1.2855
Epoch 14/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.6879 - val_loss: 1.4613

5. Evaluar¶

In [325]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_1 = train_eval
val_eval_measurement_1 = val_eval
test_eval_measurement_1 = test_eval

train_rmse_measurement_1 = train_rmse
val_rmse_measurement_1 = val_rmse
test_rmse_measurement_1 = test_rmse
2942/2942 [==============================] - 2s 613us/step
368/368 [==============================] - 0s 607us/step
368/368 [==============================] - 0s 601us/step
Evaluación Entrenamiento MAE: 3.397733211517334
Evaluación Validación MAE: 1.4613102674484253
Evaluación Prueba MAE: 3.6630656719207764
Entrenamiento RMSE: 9.165555953979492
Validación RMSE: 2.6851067543029785
Prueba RMSE: 5.405536651611328

6. Gráfica¶

In [326]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [327]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df['Measurement'].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 1))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], next_prediction)

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 8ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-16 00:01:00           NaN       151.623505
2022-03-16 00:16:00           NaN       151.369003

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [341]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement']], seq_length)
X_val, y_val = create_sequences(val[['Measurement']], seq_length)
X_test, y_test = create_sequences(test[['Measurement']], seq_length)

# 13. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 1))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))

# 14. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_2 = train_eval
val_eval_measurement_2 = val_eval
test_eval_measurement_2 = test_eval

train_rmse_measurement_2 = train_rmse
val_rmse_measurement_2 = val_rmse
test_rmse_measurement_2 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_47"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_57 (LSTM)              (None, 64)                16896     
                                                                 
 dense_47 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 16,961
Trainable params: 16,961
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 4s 3ms/step - loss: 3.2362 - val_loss: 2.1548
Epoch 2/20
1471/1471 [==============================] - 4s 3ms/step - loss: 2.3219 - val_loss: 0.9614
Epoch 3/20
1471/1471 [==============================] - 4s 3ms/step - loss: 2.3921 - val_loss: 0.4749
Epoch 4/20
1471/1471 [==============================] - 4s 3ms/step - loss: 2.1076 - val_loss: 1.0579
Epoch 5/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.9189 - val_loss: 0.3827
Epoch 6/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.8168 - val_loss: 0.5475
Epoch 7/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.6238 - val_loss: 0.5080
Epoch 8/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.5767 - val_loss: 0.9051
2942/2942 [==============================] - 3s 910us/step
368/368 [==============================] - 0s 892us/step
368/368 [==============================] - 0s 970us/step
Evaluación Entrenamiento MAE: 1.6751281023025513
Evaluación Validación MAE: 0.9051337242126465
Evaluación Prueba MAE: 0.755179762840271
Entrenamiento RMSE: 4.604981899261475
Validación RMSE: 1.474887728691101
Prueba RMSE: 1.4225993156433105

2º Optimización de hiperparámetros¶

In [78]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement']], seq_length)
X_val, y_val = create_sequences(val[['Measurement']], seq_length)
X_test, y_test = create_sequences(test[['Measurement']], seq_length)

# 13. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 1))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))

# 14. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1))
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_3 = train_eval
val_eval_measurement_3 = val_eval
test_eval_measurement_3 = test_eval

train_rmse_measurement_3 = train_rmse
val_rmse_measurement_3 = val_rmse
test_rmse_measurement_3 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_2 (LSTM)               (None, 8, 32)             4352      
                                                                 
 batch_normalization (BatchN  (None, 8, 32)            128       
 ormalization)                                                   
                                                                 
 dropout (Dropout)           (None, 8, 32)             0         
                                                                 
 lstm_3 (LSTM)               (None, 64)                24832     
                                                                 
 batch_normalization_1 (Batc  (None, 64)               256       
 hNormalization)                                                 
                                                                 
 dropout_1 (Dropout)         (None, 64)                0         
                                                                 
 dense_2 (Dense)             (None, 1)                 65        
                                                                 
=================================================================
Total params: 29,633
Trainable params: 29,441
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 7s 4ms/step - loss: 45.8393 - val_loss: 4.0092
Epoch 2/20
1471/1471 [==============================] - 5s 4ms/step - loss: 22.3210 - val_loss: 8.9894
Epoch 3/20
1471/1471 [==============================] - 5s 4ms/step - loss: 21.7114 - val_loss: 4.3612
Epoch 4/20
1471/1471 [==============================] - 5s 4ms/step - loss: 21.5136 - val_loss: 3.8598
Epoch 5/20
1471/1471 [==============================] - 5s 4ms/step - loss: 21.5876 - val_loss: 8.5723
Epoch 6/20
1471/1471 [==============================] - 5s 4ms/step - loss: 21.8220 - val_loss: 5.3411
Epoch 7/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.5005 - val_loss: 12.6982
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 12.524332046508789
Evaluación Validación MAE: 12.698190689086914
Evaluación Prueba MAE: 18.400575637817383
Entrenamiento RMSE: 15.80256175994873
Validación RMSE: 13.540170669555664
Prueba RMSE: 19.12810707092285

Algoritmo LSTM con características de entrada: 'Measurement' y 'In_Range'¶

En este caso se esta entrenando al algoritmo LSTM con las variables 'Measurement' y 'In_Range'.

  • 'Mesurement' es en nivel de glucosa.
  • 'In_Range' indica si una medición está dentro del rango de niveles normales, entre 70-180 mg/dl.

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [79]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [80]:
df
Out[80]:
Measurement In_Range
Datetime
2018-11-06 06:45:00 134 True
2018-11-06 07:00:00 134 True
2018-11-06 07:15:00 139 True
2018-11-06 07:30:00 145 True
2018-11-06 07:45:00 147 True
... ... ...
2022-03-08 11:30:00 179 True
2022-03-08 12:30:00 173 True
2022-03-08 12:45:00 171 True
2022-03-08 13:15:00 165 True
2022-03-15 23:46:00 155 True

10409 rows × 2 columns

2. Dividir el conjunto de datos¶

In [81]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [82]:
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','In_Range']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','In_Range']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','In_Range']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_3"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_4 (LSTM)               (None, 64)                17152     
                                                                 
 dense_3 (Dense)             (None, 1)                 65        
                                                                 
=================================================================
Total params: 17,217
Trainable params: 17,217
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [83]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1471/1471 [==============================] - 4s 2ms/step - loss: 3.5523 - val_loss: 1.6889
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2653 - val_loss: 0.7579
Epoch 3/20
1471/1471 [==============================] - 4s 2ms/step - loss: 2.3494 - val_loss: 2.6885
Epoch 4/20
1471/1471 [==============================] - 4s 2ms/step - loss: 2.1578 - val_loss: 0.8448
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 1.9449 - val_loss: 0.3595
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 1.6986 - val_loss: 0.7306
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 1.6241 - val_loss: 1.4357
Epoch 8/20
1471/1471 [==============================] - 4s 2ms/step - loss: 1.6380 - val_loss: 0.5561

5. Evaluar¶

In [84]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_in_range_2 = train_eval
val_eval_measurement_in_range_2 = val_eval
test_eval_measurement_in_range_2 = test_eval

train_rmse_measurement_in_range_2 = train_rmse
val_rmse_measurement_in_range_2 = val_rmse
test_rmse_measurement_in_range_2 = test_rmse
2942/2942 [==============================] - 2s 760us/step
368/368 [==============================] - 0s 751us/step
368/368 [==============================] - 0s 746us/step
Evaluación Entrenamiento MAE: 1.232211709022522
Evaluación Validación MAE: 0.5560938119888306
Evaluación Prueba MAE: 0.7522381544113159
Entrenamiento RMSE: 4.56373929977417
Validación RMSE: 1.2793986797332764
Prueba RMSE: 1.3832286596298218

6. Gráfica¶

In [85]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [86]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement','In_Range']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 2))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1]]], axis=0)

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 9ms/step
1/1 [==============================] - 0s 10ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-16 00:01:00           NaN       157.013489
2022-03-16 00:16:00           NaN       157.832733

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [87]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','In_Range']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','In_Range']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','In_Range']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_in_range_1 = train_eval
val_eval_measurement_in_range_1 = val_eval
test_eval_measurement_in_range_1 = test_eval

train_rmse_measurement_in_range_1 = train_rmse
val_rmse_measurement_in_range_1 = val_rmse
test_rmse_measurement_in_range_1 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_4"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_5 (LSTM)               (None, 32)                4480      
                                                                 
 dense_4 (Dense)             (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,513
Trainable params: 4,513
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 3s 2ms/step - loss: 138.2115 - val_loss: 127.9240
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 110.2278 - val_loss: 101.1393
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 81.9239 - val_loss: 71.8314
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 53.8821 - val_loss: 43.9747
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 28.7584 - val_loss: 18.8059
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 15.2615 - val_loss: 7.9287
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 9.4990 - val_loss: 4.8718
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 6.6924 - val_loss: 2.8957
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.9679 - val_loss: 1.8840
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.1110 - val_loss: 1.2146
Epoch 11/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.4092 - val_loss: 1.3017
Epoch 12/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.0485 - val_loss: 1.3891
Epoch 13/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.8782 - val_loss: 0.8578
Epoch 14/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6136 - val_loss: 0.6092
Epoch 15/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.5512 - val_loss: 0.4022
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2845 - val_loss: 0.9302
Epoch 17/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2159 - val_loss: 0.7827
Epoch 18/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1603 - val_loss: 0.3276
Epoch 19/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1245 - val_loss: 0.6638
Epoch 20/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1497 - val_loss: 0.3016
2942/2942 [==============================] - 2s 632us/step
368/368 [==============================] - 0s 620us/step
368/368 [==============================] - 0s 614us/step
Evaluación Entrenamiento MAE: 1.6543866395950317
Evaluación Validación MAE: 0.3015759587287903
Evaluación Prueba MAE: 0.5337554216384888
Entrenamiento RMSE: 6.209878444671631
Validación RMSE: 1.3075438737869263
Prueba RMSE: 1.662801742553711

2º Optimización de hiperparámetros¶

In [88]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','In_Range']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','In_Range']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','In_Range']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_in_range_3 = train_eval
val_eval_measurement_in_range_3 = val_eval
test_eval_measurement_in_range_3 = test_eval

train_rmse_measurement_in_range_3 = train_rmse
val_rmse_measurement_in_range_3 = val_rmse
test_rmse_measurement_in_range_3 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_5"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_6 (LSTM)               (None, 8, 32)             4480      
                                                                 
 batch_normalization_2 (Batc  (None, 8, 32)            128       
 hNormalization)                                                 
                                                                 
 dropout_2 (Dropout)         (None, 8, 32)             0         
                                                                 
 lstm_7 (LSTM)               (None, 64)                24832     
                                                                 
 batch_normalization_3 (Batc  (None, 64)               256       
 hNormalization)                                                 
                                                                 
 dropout_3 (Dropout)         (None, 64)                0         
                                                                 
 dense_5 (Dense)             (None, 1)                 65        
                                                                 
=================================================================
Total params: 29,761
Trainable params: 29,569
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 6s 4ms/step - loss: 46.5253 - val_loss: 4.5927
Epoch 2/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.2459 - val_loss: 10.4085
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.4915 - val_loss: 2.7437
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.1844 - val_loss: 4.5216
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.0268 - val_loss: 3.9724
Epoch 6/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.0547 - val_loss: 4.5230
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 7.778632640838623
Evaluación Validación MAE: 4.523031711578369
Evaluación Prueba MAE: 9.269298553466797
Entrenamiento RMSE: 10.855286598205566
Validación RMSE: 5.989138126373291
Prueba RMSE: 10.361227989196777

Algoritmo LSTM con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'¶

En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal
  • "Hyperglycemia" indica si una medición está por encima del rango normal.

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [328]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [329]:
df
Out[329]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-11-06 06:45:00 134 False False
2018-11-06 07:00:00 134 False False
2018-11-06 07:15:00 139 False False
2018-11-06 07:30:00 145 False False
2018-11-06 07:45:00 147 False False
... ... ... ...
2022-03-08 11:30:00 179 False False
2022-03-08 12:30:00 173 False False
2022-03-08 12:45:00 171 False False
2022-03-08 13:15:00 165 False False
2022-03-15 23:46:00 155 False False

10409 rows × 3 columns

2. Dividir el conjunto de datos¶

In [330]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [331]:
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_46"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_56 (LSTM)              (None, 64)                17408     
                                                                 
 dense_46 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [332]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1471/1471 [==============================] - 5s 3ms/step - loss: 3.3848 - val_loss: 1.9171
Epoch 2/20
1471/1471 [==============================] - 4s 3ms/step - loss: 2.0156 - val_loss: 0.9414
Epoch 3/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.8913 - val_loss: 0.3186
Epoch 4/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.7138 - val_loss: 0.3235
Epoch 5/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.8326 - val_loss: 0.1901
Epoch 6/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.6533 - val_loss: 1.6578
Epoch 7/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.5178 - val_loss: 0.9782
Epoch 8/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.4218 - val_loss: 0.2717

5. Evaluar¶

In [333]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2 = train_eval
val_eval_measurement_hypo_hype_2 = val_eval
test_eval_measurement_hypo_hype_2 = test_eval

train_rmse_measurement_hypo_hype_2 = train_rmse
val_rmse_measurement_hypo_hype_2 = val_rmse
test_rmse_measurement_hypo_hype_2 = test_rmse
2942/2942 [==============================] - 2s 810us/step
368/368 [==============================] - 0s 867us/step
368/368 [==============================] - 0s 852us/step
Evaluación Entrenamiento MAE: 1.083967924118042
Evaluación Validación MAE: 0.27166786789894104
Evaluación Prueba MAE: 0.23148754239082336
Entrenamiento RMSE: 4.52763557434082
Validación RMSE: 1.2004188299179077
Prueba RMSE: 1.2222604751586914

6. Gráfica¶

In [334]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [335]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')

# Almacena dato
future_predictions_hypo_hype_2_LSTM_22 = future_predictions
plt.show()
1/1 [==============================] - 0s 11ms/step
1/1 [==============================] - 0s 9ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-16 00:01:00           NaN       156.141495
2022-03-16 00:16:00           NaN       157.410568

6.2 Clarke Error Grid¶

In [336]:
def clarke_error_grid(ref_values, pred_values):
    assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))

    if max(ref_values) > 400 or max(pred_values) > 400:
        print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
    if min(ref_values) < 0 or min(pred_values) < 0:
        print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values),  min(pred_values)))

    plt.clf()
    plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
    plt.title("Clarke Error Grid")
    plt.xlabel("Reference Concentration (mg/dl)")
    plt.ylabel("Prediction Concentration (mg/dl)")
    plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.gca().set_facecolor('white')
    plt.gca().set_xlim([0, 400])
    plt.gca().set_ylim([0, 400])
    plt.gca().set_aspect((400)/(400))
    plt.plot([0, 400], [0, 400], ':', c='black')
    plt.plot([0, 175/3], [70, 70], '-', c='black')
    plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
    plt.plot([70, 70], [84, 400],'-', c='black')
    plt.plot([0, 70], [180, 180], '-', c='black')
    plt.plot([70, 290], [180, 400],'-', c='black')
    plt.plot([70, 70], [0, 56], '-', c='black')
    plt.plot([70, 400], [56, 320],'-', c='black')
    plt.plot([180, 180], [0, 70], '-', c='black')
    plt.plot([180, 400], [70, 70], '-', c='black')
    plt.plot([240, 240], [70, 180],'-', c='black')
    plt.plot([240, 400], [180, 180], '-', c='black')
    plt.plot([130, 180], [0, 70], '-', c='black')
    plt.text(30, 15, "A", fontsize=15)
    plt.text(370, 260, "B", fontsize=15)
    plt.text(280, 370, "B", fontsize=15)
    plt.text(160, 370, "C", fontsize=15)
    plt.text(160, 15, "C", fontsize=15)
    plt.text(30, 140, "D", fontsize=15)
    plt.text(370, 120, "D", fontsize=15)
    plt.text(30, 370, "E", fontsize=15)
    plt.text(370, 15, "E", fontsize=15)

    zone = [0] * 5
    for i in range(len(ref_values)):
        if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
            zone[0] += 1  # Zone A
        elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
            zone[4] += 1  # Zone E
        elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
            zone[2] += 1  # Zone C
        elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
            zone[3] += 1  # Zone D
        else:
            zone[1] += 1  # Zone B

    return plt, zone

ref_values = y_test
pred_values = y_pred_test

plt, zone = clarke_error_grid(ref_values, pred_values)

# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_22 = plt.gcf()

# Mostrar la figura
plt.show()

# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})

# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total

# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))

# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_22 = zone_df

# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_22.style.hide(axis="index"))
Conteo de Zonas:
Zona Conteo Proporción
A 11758 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [98]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1 = train_eval
val_eval_measurement_hypo_hype_1 = val_eval
test_eval_measurement_hypo_hype_1 = test_eval

train_rmse_measurement_hypo_hype_1 = train_rmse
val_rmse_measurement_hypo_hype_1 = val_rmse
test_rmse_measurement_hypo_hype_1 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_7"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_9 (LSTM)               (None, 32)                4608      
                                                                 
 dense_7 (Dense)             (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 3s 2ms/step - loss: 132.2171 - val_loss: 116.9254
Epoch 2/20
1471/1471 [==============================] - 2s 2ms/step - loss: 93.7074 - val_loss: 79.4056
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 57.6149 - val_loss: 44.0134
Epoch 4/20
1471/1471 [==============================] - 2s 2ms/step - loss: 26.5661 - val_loss: 14.2901
Epoch 5/20
1471/1471 [==============================] - 2s 2ms/step - loss: 13.1676 - val_loss: 7.3467
Epoch 6/20
1471/1471 [==============================] - 2s 2ms/step - loss: 8.9232 - val_loss: 4.5158
Epoch 7/20
1471/1471 [==============================] - 2s 2ms/step - loss: 6.5820 - val_loss: 2.9944
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.8992 - val_loss: 1.8385
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.7555 - val_loss: 1.1614
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.3712 - val_loss: 1.0437
Epoch 11/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.8549 - val_loss: 1.0787
Epoch 12/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.6591 - val_loss: 0.6316
Epoch 13/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.4153 - val_loss: 0.9110
Epoch 14/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.3556 - val_loss: 0.4507
Epoch 15/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.1673 - val_loss: 0.7386
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1262 - val_loss: 0.4601
Epoch 17/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.0089 - val_loss: 0.6385
2942/2942 [==============================] - 2s 597us/step
368/368 [==============================] - 0s 605us/step
368/368 [==============================] - 0s 604us/step
Evaluación Entrenamiento MAE: 1.9722120761871338
Evaluación Validación MAE: 0.6385322213172913
Evaluación Prueba MAE: 1.0579259395599365
Entrenamiento RMSE: 6.278008937835693
Validación RMSE: 1.4417616128921509
Prueba RMSE: 1.9623427391052246

2º Optimización de hiperparámetros¶

In [99]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt


# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3 = train_eval
val_eval_measurement_hypo_hype_3 = val_eval
test_eval_measurement_hypo_hype_3 = test_eval

train_rmse_measurement_hypo_hype_3 = train_rmse
val_rmse_measurement_hypo_hype_3 = val_rmse
test_rmse_measurement_hypo_hype_3 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_8"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_10 (LSTM)              (None, 8, 32)             4608      
                                                                 
 batch_normalization_4 (Batc  (None, 8, 32)            128       
 hNormalization)                                                 
                                                                 
 dropout_4 (Dropout)         (None, 8, 32)             0         
                                                                 
 lstm_11 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_5 (Batc  (None, 64)               256       
 hNormalization)                                                 
                                                                 
 dropout_5 (Dropout)         (None, 64)                0         
                                                                 
 dense_8 (Dense)             (None, 1)                 65        
                                                                 
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 6s 4ms/step - loss: 46.2266 - val_loss: 9.3853
Epoch 2/20
1471/1471 [==============================] - 5s 4ms/step - loss: 22.6362 - val_loss: 12.1679
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.9584 - val_loss: 9.6945
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.7004 - val_loss: 4.8474
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.4922 - val_loss: 12.1168
Epoch 6/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.3319 - val_loss: 3.7308
Epoch 7/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.2087 - val_loss: 11.0984
Epoch 8/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.1116 - val_loss: 6.3882
Epoch 9/20
1471/1471 [==============================] - 6s 4ms/step - loss: 20.9531 - val_loss: 4.2203
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 7.9226298332214355
Evaluación Validación MAE: 4.220335006713867
Evaluación Prueba MAE: 6.241281509399414
Entrenamiento RMSE: 10.941481590270996
Validación RMSE: 5.068966388702393
Prueba RMSE: 6.4750542640686035

Algoritmo LSTM con características de entrada: 'Measurement' y 'Hour_of_Day'¶

En este caso se esta entrenando al algoritmo LSTM con las variables 'Measurement' y 'Hour_of_Day'.

  • 'Mesurement' es el nivel de glucosa.
  • 'Hour_of_Day' para indicar la hora del día en que se tomó cada medición.

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [100]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hour_of_Day']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [101]:
df
Out[101]:
Measurement Hour_of_Day
Datetime
2018-11-06 06:45:00 134 6
2018-11-06 07:00:00 134 7
2018-11-06 07:15:00 139 7
2018-11-06 07:30:00 145 7
2018-11-06 07:45:00 147 7
... ... ...
2022-03-08 11:30:00 179 11
2022-03-08 12:30:00 173 12
2022-03-08 12:45:00 171 12
2022-03-08 13:15:00 165 13
2022-03-15 23:46:00 155 23

10409 rows × 2 columns

2. Dividir el conjunto de datos¶

In [102]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [103]:
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Hour_of_Day']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Hour_of_Day']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Hour_of_Day']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_9"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_12 (LSTM)              (None, 64)                17152     
                                                                 
 dense_9 (Dense)             (None, 1)                 65        
                                                                 
=================================================================
Total params: 17,217
Trainable params: 17,217
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [104]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1471/1471 [==============================] - 4s 2ms/step - loss: 5.0938 - val_loss: 1.2173
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2982 - val_loss: 0.6090
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0070 - val_loss: 0.6723
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 1.8999 - val_loss: 0.3173
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.3002 - val_loss: 1.2668
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1917 - val_loss: 1.1737
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0090 - val_loss: 1.0570

5. Evaluar¶

In [105]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hour_of_day_2 = train_eval
val_eval_measurement_hour_of_day_2 = val_eval
test_eval_measurement_hour_of_day_2 = test_eval

train_rmse_measurement_hour_of_day_2 = train_rmse
val_rmse_measurement_hour_of_day_2 = val_rmse
test_rmse_measurement_hour_of_day_2 = test_rmse
2942/2942 [==============================] - 2s 746us/step
368/368 [==============================] - 0s 836us/step
368/368 [==============================] - 0s 850us/step
Evaluación Entrenamiento MAE: 1.9983710050582886
Evaluación Validación MAE: 1.0570324659347534
Evaluación Prueba MAE: 1.3511601686477661
Entrenamiento RMSE: 5.443111896514893
Validación RMSE: 1.727100133895874
Prueba RMSE: 1.9237534999847412

6. Gráfica¶

In [106]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [107]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement','Hour_of_Day']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 2))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1]]], axis=0)

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-16 00:01:00           NaN       166.313065
2022-03-16 00:16:00           NaN       166.716141

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [108]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hour_of_Day']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Hour_of_Day']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Hour_of_Day']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Hour_of_Day']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hour_of_day_1 = train_eval
val_eval_measurement_hour_of_day_1 = val_eval
test_eval_measurement_hour_of_day_1 = test_eval

train_rmse_measurement_hour_of_day_1 = train_rmse
val_rmse_measurement_hour_of_day_1 = val_rmse
test_rmse_measurement_hour_of_day_1 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_10"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_13 (LSTM)              (None, 32)                4480      
                                                                 
 dense_10 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,513
Trainable params: 4,513
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 3s 2ms/step - loss: 134.4768 - val_loss: 121.8707
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 101.3275 - val_loss: 89.0795
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 68.1067 - val_loss: 55.9350
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 36.8675 - val_loss: 24.5940
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 19.2243 - val_loss: 12.2342
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 13.6366 - val_loss: 7.5060
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 9.2897 - val_loss: 4.5421
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 6.5768 - val_loss: 3.8769
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.8175 - val_loss: 1.8758
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.7758 - val_loss: 1.7478
Epoch 11/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.3433 - val_loss: 1.1201
Epoch 12/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.0773 - val_loss: 0.7236
Epoch 13/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.7448 - val_loss: 0.5562
Epoch 14/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6129 - val_loss: 1.0078
Epoch 15/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.1278 - val_loss: 0.9319
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.7623 - val_loss: 1.4178
2942/2942 [==============================] - 2s 600us/step
368/368 [==============================] - 0s 620us/step
368/368 [==============================] - 0s 649us/step
Evaluación Entrenamiento MAE: 3.570218086242676
Evaluación Validación MAE: 1.4177560806274414
Evaluación Prueba MAE: 1.9302120208740234
Entrenamiento RMSE: 7.898557662963867
Validación RMSE: 2.019634246826172
Prueba RMSE: 3.149078607559204

2º Optimización de hiperparámetros¶

In [109]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hour_of_Day']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Hour_of_Day']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Hour_of_Day']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Hour_of_Day']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hour_of_day_3 = train_eval
val_eval_measurement_hour_of_day_3 = val_eval
test_eval_measurement_hour_of_day_3 = test_eval

train_rmse_measurement_hour_of_day_3 = train_rmse
val_rmse_measurement_hour_of_day_3 = val_rmse
test_rmse_measurement_hour_of_day_3 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_11"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_14 (LSTM)              (None, 8, 32)             4480      
                                                                 
 batch_normalization_6 (Batc  (None, 8, 32)            128       
 hNormalization)                                                 
                                                                 
 dropout_6 (Dropout)         (None, 8, 32)             0         
                                                                 
 lstm_15 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_7 (Batc  (None, 64)               256       
 hNormalization)                                                 
                                                                 
 dropout_7 (Dropout)         (None, 64)                0         
                                                                 
 dense_11 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 29,761
Trainable params: 29,569
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 7s 4ms/step - loss: 46.1576 - val_loss: 6.3130
Epoch 2/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.0568 - val_loss: 2.4649
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.6295 - val_loss: 2.3236
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.2521 - val_loss: 4.1637
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.0167 - val_loss: 4.2129
Epoch 6/20
1471/1471 [==============================] - 6s 4ms/step - loss: 20.8337 - val_loss: 3.9539
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 5.086625099182129
Evaluación Validación MAE: 3.9538815021514893
Evaluación Prueba MAE: 6.964335918426514
Entrenamiento RMSE: 7.240564346313477
Validación RMSE: 4.714972019195557
Prueba RMSE: 7.407690525054932

Algoritmo LSTM con características de entrada: 'Measurement' y 'Day_of_Week'¶

En este caso se esta entrenando al algoritmo LSTM con las variables 'Measurement' y 'Day_of_Week'.

  • 'Mesurement' es el nivel de glucosa.
  • 'Day_of_Week' para indicar el día de la semana. 0 es lunes y 6 es domingo.

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [110]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Day_of_Week']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [111]:
df
Out[111]:
Measurement Day_of_Week
Datetime
2018-11-06 06:45:00 134 1
2018-11-06 07:00:00 134 1
2018-11-06 07:15:00 139 1
2018-11-06 07:30:00 145 1
2018-11-06 07:45:00 147 1
... ... ...
2022-03-08 11:30:00 179 1
2022-03-08 12:30:00 173 1
2022-03-08 12:45:00 171 1
2022-03-08 13:15:00 165 1
2022-03-15 23:46:00 155 1

10409 rows × 2 columns

2. Dividir el conjunto de datos¶

In [112]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [113]:
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Day_of_Week']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_12"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_16 (LSTM)              (None, 64)                17152     
                                                                 
 dense_12 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 17,217
Trainable params: 17,217
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [114]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1471/1471 [==============================] - 4s 3ms/step - loss: 3.9463 - val_loss: 0.7817
Epoch 2/20
1471/1471 [==============================] - 4s 3ms/step - loss: 3.2367 - val_loss: 0.5049
Epoch 3/20
1471/1471 [==============================] - 4s 2ms/step - loss: 2.3581 - val_loss: 1.2754
Epoch 4/20
1471/1471 [==============================] - 4s 2ms/step - loss: 2.3379 - val_loss: 0.3002
Epoch 5/20
1471/1471 [==============================] - 4s 2ms/step - loss: 1.9858 - val_loss: 0.5236
Epoch 6/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.9003 - val_loss: 2.0343
Epoch 7/20
1471/1471 [==============================] - 4s 2ms/step - loss: 1.8998 - val_loss: 0.7044

5. Evaluar¶

In [115]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_day_of_week_2 = train_eval
val_eval_measurement_day_of_week_2 = val_eval
test_eval_measurement_day_of_week_2 = test_eval

train_rmse_measurement_day_of_week_2 = train_rmse
val_rmse_measurement_day_of_week_2 = val_rmse
test_rmse_measurement_day_of_week_2 = test_rmse
2942/2942 [==============================] - 2s 779us/step
368/368 [==============================] - 0s 797us/step
368/368 [==============================] - 0s 819us/step
Evaluación Entrenamiento MAE: 1.6692419052124023
Evaluación Validación MAE: 0.7044217586517334
Evaluación Prueba MAE: 0.7746291756629944
Entrenamiento RMSE: 5.377539157867432
Validación RMSE: 1.4903850555419922
Prueba RMSE: 1.5528700351715088

6. Gráfica¶

In [116]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [117]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement','Day_of_Week']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 2))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1]]], axis=0)

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-16 00:01:00           NaN       160.964401
2022-03-16 00:16:00           NaN       159.243057

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [118]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Day_of_Week']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Day_of_Week']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_day_of_week_1 = train_eval
val_eval_measurement_day_of_week_1 = val_eval
test_eval_measurement_day_of_week_1 = test_eval

train_rmse_measurement_day_of_week_1 = train_rmse
val_rmse_measurement_day_of_week_1 = val_rmse
test_rmse_measurement_day_of_week_1 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_13"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_17 (LSTM)              (None, 32)                4480      
                                                                 
 dense_13 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,513
Trainable params: 4,513
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 4s 2ms/step - loss: 131.7301 - val_loss: 116.5562
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 94.3545 - val_loss: 80.7936
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 59.1237 - val_loss: 45.6152
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 27.6796 - val_loss: 15.1195
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 14.1706 - val_loss: 7.6964
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 10.1934 - val_loss: 5.4373
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 7.6666 - val_loss: 3.7413
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 5.7240 - val_loss: 2.5617
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.3452 - val_loss: 2.3716
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.4809 - val_loss: 1.7590
Epoch 11/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.0224 - val_loss: 0.7280
Epoch 12/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6887 - val_loss: 0.6233
Epoch 13/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.5725 - val_loss: 0.8415
Epoch 14/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.4848 - val_loss: 0.4222
Epoch 15/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2404 - val_loss: 0.7913
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1203 - val_loss: 0.4545
Epoch 17/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0784 - val_loss: 0.2821
Epoch 18/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0595 - val_loss: 0.9754
Epoch 19/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0513 - val_loss: 0.8418
Epoch 20/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0260 - val_loss: 1.7636
2942/2942 [==============================] - 2s 602us/step
368/368 [==============================] - 0s 605us/step
368/368 [==============================] - 0s 615us/step
Evaluación Entrenamiento MAE: 3.097968339920044
Evaluación Validación MAE: 1.7635772228240967
Evaluación Prueba MAE: 1.5313645601272583
Entrenamiento RMSE: 6.313864231109619
Validación RMSE: 2.127568483352661
Prueba RMSE: 2.175009250640869

2º Optimización de hiperparámetros¶

In [119]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Day_of_Week']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Day_of_Week']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_day_of_week_3 = train_eval
val_eval_measurement_day_of_week_3 = val_eval
test_eval_measurement_day_of_week_3 = test_eval

train_rmse_measurement_day_of_week_3 = train_rmse
val_rmse_measurement_day_of_week_3 = val_rmse
test_rmse_measurement_day_of_week_3 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_14"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_18 (LSTM)              (None, 8, 32)             4480      
                                                                 
 batch_normalization_8 (Batc  (None, 8, 32)            128       
 hNormalization)                                                 
                                                                 
 dropout_8 (Dropout)         (None, 8, 32)             0         
                                                                 
 lstm_19 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_9 (Batc  (None, 64)               256       
 hNormalization)                                                 
                                                                 
 dropout_9 (Dropout)         (None, 64)                0         
                                                                 
 dense_14 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 29,761
Trainable params: 29,569
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 7s 4ms/step - loss: 46.9868 - val_loss: 10.4243
Epoch 2/20
1471/1471 [==============================] - 6s 4ms/step - loss: 23.1711 - val_loss: 5.7973
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.5526 - val_loss: 6.8161
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.0047 - val_loss: 6.2960
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.8285 - val_loss: 7.2971
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 10.655786514282227
Evaluación Validación MAE: 7.297052383422852
Evaluación Prueba MAE: 13.426828384399414
Entrenamiento RMSE: 15.592889785766602
Validación RMSE: 8.962603569030762
Prueba RMSE: 14.679518699645996

Algoritmo LSTM con características de entrada: 'Measurement', 'In_Range', 'Hypoglycemia' y 'Hyperglycemia', 'Hour_of_Day' y 'Day_of_Week'.¶

En este caso se esta entrenando al algoritmo LSTM con las variables 'Measurement', 'In_Range', 'Hypoglycemia' y 'Hyperglycemia', 'Hour_of_Day' y 'Day_of_Week'.

  • 'Mesurement' es el nivel de glucosa.
  • "In_Range" indica si una medición está dentro del rango de niveles normales.
  • "Hyperglycemia" indica si una medición está por encima del rango normal.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal
  • "Hour_of_Day" para indicar la hora del día en que se tomó cada medición.
  • "Day_of_Week" para indicar el día de la semana. 0 es lunes y 6 es domingo.

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [120]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [121]:
df
Out[121]:
Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-11-06 06:45:00 134 True False False 6 1
2018-11-06 07:00:00 134 True False False 7 1
2018-11-06 07:15:00 139 True False False 7 1
2018-11-06 07:30:00 145 True False False 7 1
2018-11-06 07:45:00 147 True False False 7 1
... ... ... ... ... ... ...
2022-03-08 11:30:00 179 True False False 11 1
2022-03-08 12:30:00 173 True False False 12 1
2022-03-08 12:45:00 171 True False False 12 1
2022-03-08 13:15:00 165 True False False 13 1
2022-03-15 23:46:00 155 True False False 23 1

10409 rows × 6 columns

2. Dividir el conjunto de datos¶

In [122]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [123]:
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 6))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 6))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 6))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_15"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_20 (LSTM)              (None, 64)                18176     
                                                                 
 dense_15 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 18,241
Trainable params: 18,241
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [124]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1471/1471 [==============================] - 4s 2ms/step - loss: 4.3364 - val_loss: 0.6861
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.2986 - val_loss: 0.4602
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.7819 - val_loss: 1.0866
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6742 - val_loss: 1.1946
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6183 - val_loss: 0.8242

5. Evaluar¶

In [125]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_combined_2 = train_eval
val_eval_measurement_combined_2 = val_eval
test_eval_measurement_combined_2 = test_eval

train_rmse_measurement_combined_2 = train_rmse
val_rmse_measurement_combined_2 = val_rmse
test_rmse_measurement_combined_2 = test_rmse
2942/2942 [==============================] - 2s 797us/step
368/368 [==============================] - 0s 795us/step
368/368 [==============================] - 0s 784us/step
Evaluación Entrenamiento MAE: 2.2896623611450195
Evaluación Validación MAE: 0.8242446780204773
Evaluación Prueba MAE: 0.6581457853317261
Entrenamiento RMSE: 6.420724391937256
Validación RMSE: 1.8279951810836792
Prueba RMSE: 1.839697003364563

6. Gráfica¶

In [126]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [127]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 6))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2], last_sequence[-1][3], last_sequence[-1][4], last_sequence[-1][5]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 9ms/step
1/1 [==============================] - 0s 10ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-16 00:01:00           NaN       166.969177
2022-03-16 00:16:00           NaN       172.455338

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [128]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 6)) # 6 features
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 6)) # 6 features
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 6)) # 6 features

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_combined_1 = train_eval
val_eval_measurement_combined_1 = val_eval
test_eval_measurement_combined_1 = test_eval

train_rmse_measurement_combined_1 = train_rmse
val_rmse_measurement_combined_1 = val_rmse
test_rmse_measurement_combined_1 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_16"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_21 (LSTM)              (None, 32)                4992      
                                                                 
 dense_16 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 5,025
Trainable params: 5,025
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 4s 2ms/step - loss: 137.2627 - val_loss: 126.2613
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 108.4822 - val_loss: 99.3464
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 77.6350 - val_loss: 65.0552
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 45.7832 - val_loss: 34.2348
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 21.7077 - val_loss: 12.2683
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 13.8918 - val_loss: 7.6678
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 10.3435 - val_loss: 5.6352
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 7.9973 - val_loss: 4.3661
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 6.2528 - val_loss: 3.0041
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.9268 - val_loss: 2.6749
Epoch 11/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.1266 - val_loss: 1.6302
Epoch 12/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.2765 - val_loss: 1.0367
Epoch 13/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.9762 - val_loss: 0.9002
Epoch 14/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.8334 - val_loss: 0.8995
Epoch 15/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6069 - val_loss: 0.8952
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.5612 - val_loss: 0.6054
Epoch 17/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.4186 - val_loss: 0.6025
Epoch 18/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.3298 - val_loss: 0.8918
Epoch 19/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1645 - val_loss: 0.7523
Epoch 20/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2164 - val_loss: 0.6855
2942/2942 [==============================] - 2s 632us/step
368/368 [==============================] - 0s 606us/step
368/368 [==============================] - 0s 614us/step
Evaluación Entrenamiento MAE: 2.390277147293091
Evaluación Validación MAE: 0.6855114102363586
Evaluación Prueba MAE: 1.2360053062438965
Entrenamiento RMSE: 6.2993340492248535
Validación RMSE: 1.504172921180725
Prueba RMSE: 2.051300048828125

2º Optimización de hiperparámetros¶

In [129]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 6)) # 6 features
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 6)) # 6 features
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 6)) # 6 features

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_combined_3 = train_eval
val_eval_measurement_combined_3 = val_eval
test_eval_measurement_combined_3 = test_eval

train_rmse_measurement_combined_3 = train_rmse
val_rmse_measurement_combined_3 = val_rmse
test_rmse_measurement_combined_3 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_17"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_22 (LSTM)              (None, 8, 32)             4992      
                                                                 
 batch_normalization_10 (Bat  (None, 8, 32)            128       
 chNormalization)                                                
                                                                 
 dropout_10 (Dropout)        (None, 8, 32)             0         
                                                                 
 lstm_23 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_11 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_11 (Dropout)        (None, 64)                0         
                                                                 
 dense_17 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 30,273
Trainable params: 30,081
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 8s 4ms/step - loss: 46.2283 - val_loss: 9.3527
Epoch 2/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.9136 - val_loss: 15.1247
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.1880 - val_loss: 28.7162
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 23.0935 - val_loss: 7.8719
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 23.0679 - val_loss: 8.3499
Epoch 6/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.8313 - val_loss: 8.2024
Epoch 7/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.7284 - val_loss: 7.0599
Epoch 8/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.4004 - val_loss: 6.6235
Epoch 9/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.4001 - val_loss: 9.3511
Epoch 10/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.2407 - val_loss: 6.1467
Epoch 11/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.1233 - val_loss: 7.3190
Epoch 12/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.8646 - val_loss: 4.9952
Epoch 13/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.8429 - val_loss: 14.1133
Epoch 14/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.7566 - val_loss: 10.1010
Epoch 15/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.5854 - val_loss: 6.9462
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 12.632179260253906
Evaluación Validación MAE: 6.946190357208252
Evaluación Prueba MAE: 12.303682327270508
Entrenamiento RMSE: 26.462608337402344
Validación RMSE: 8.469077110290527
Prueba RMSE: 13.66413402557373

Resultado obtenido con 3 arquitecturas distintas de la LSTM¶

Después de realizar multiples pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement'
  • 'Measurement' + In_Range
  • 'Measurement' + Hypoglycemia + Hyperglycemia
  • 'Measurement' + Hour_of_Day
  • 'Measurement' + Day_of_Week
  • 'Measurement' + In_Range + Hypoglycemia + Hyperglycemia + Hour_of_Day + Day_of_Week

El hecho de hacer pruebas con cada una de ellas se debe a que se quería inferir si el uso de alguna de las características de entrada ofrecía unos resultados muy distintos, pero después de llevar a cabo cada una de las características de entrada por separado se puede determinar que todas dan resultados similares lo cual hace pensar que juntar todas puede dar mejores resultados.

Las arquitecturas LSTM utilizadas son:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 64. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • return_sequences: True.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5.
    • units: 64. Número de neuronas en la capa LSTM.
    • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [344]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla vacía
tabla = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement',
    'Measurement + In_Range',
    'Measurement + Hypo_Hyper',
    'Measurement + Hour_of_day',
    'Measurement + Day_of_week',
    'Todas combinaciones'
])

# Definir los valores en cada posición
tabla.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla.loc[1, 'Measurement'] = train_eval_measurement_1
tabla.loc[2, 'Measurement'] = val_eval_measurement_1
tabla.loc[3, 'Measurement'] = test_eval_measurement_1

tabla.loc[4, 'Measurement'] = train_rmse_measurement_1
tabla.loc[5, 'Measurement'] = val_rmse_measurement_1
tabla.loc[6, 'Measurement'] = test_rmse_measurement_1

tabla.loc[1, 'Measurement + In_Range'] = train_eval_measurement_in_range_1
tabla.loc[2, 'Measurement + In_Range'] = val_eval_measurement_in_range_1
tabla.loc[3, 'Measurement + In_Range'] = test_eval_measurement_in_range_1

tabla.loc[4, 'Measurement + In_Range'] = train_rmse_measurement_in_range_1
tabla.loc[5, 'Measurement + In_Range'] = val_rmse_measurement_in_range_1
tabla.loc[6, 'Measurement + In_Range'] = test_rmse_measurement_in_range_1

tabla.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1
tabla.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1
tabla.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1

tabla.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1
tabla.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1
tabla.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1

tabla.loc[1, 'Measurement + Hour_of_day'] = train_eval_measurement_hour_of_day_1
tabla.loc[2, 'Measurement + Hour_of_day'] = val_eval_measurement_hour_of_day_1
tabla.loc[3, 'Measurement + Hour_of_day'] = test_eval_measurement_hour_of_day_1

tabla.loc[4, 'Measurement + Hour_of_day'] = train_rmse_measurement_hour_of_day_1
tabla.loc[5, 'Measurement + Hour_of_day'] = val_rmse_measurement_hour_of_day_1
tabla.loc[6, 'Measurement + Hour_of_day'] = test_rmse_measurement_hour_of_day_1

tabla.loc[1, 'Measurement + Day_of_week'] = train_eval_measurement_day_of_week_1
tabla.loc[2, 'Measurement + Day_of_week'] = val_eval_measurement_day_of_week_1
tabla.loc[3, 'Measurement + Day_of_week'] = test_eval_measurement_day_of_week_1

tabla.loc[4, 'Measurement + Day_of_week'] = train_rmse_measurement_day_of_week_1
tabla.loc[5, 'Measurement + Day_of_week'] = val_rmse_measurement_day_of_week_1
tabla.loc[6, 'Measurement + Day_of_week'] = test_rmse_measurement_day_of_week_1

tabla.loc[1, 'Todas combinaciones'] = train_eval_measurement_combined_1
tabla.loc[2, 'Todas combinaciones'] = val_eval_measurement_combined_1
tabla.loc[3, 'Todas combinaciones'] = test_eval_measurement_combined_1

tabla.loc[4, 'Todas combinaciones'] = train_rmse_measurement_combined_1
tabla.loc[5, 'Todas combinaciones'] = val_rmse_measurement_combined_1
tabla.loc[6, 'Todas combinaciones'] = test_rmse_measurement_combined_1

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child)', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, LSTM
Evaluación+Métrica Measurement Measurement + In_Range Measurement + Hypo_Hyper Measurement + Hour_of_day Measurement + Day_of_week Todas combinaciones
Entrenamiento MAE 3.397733 1.654387 1.972212 3.570218 3.097968 2.390277
Validación MAE 1.461310 0.301576 0.638532 1.417756 1.763577 0.685511
Test MAE 3.663066 0.533755 1.057926 1.930212 1.531365 1.236005
Entrenamiento RMSE 9.165556 6.209878 6.278009 7.898558 6.313864 6.299334
Validación RMSE 2.685107 1.307544 1.441762 2.019634 2.127568 1.504173
Test RMSE 5.405537 1.662802 1.962343 3.149079 2.175009 2.051300
In [345]:
import pandas as pd
from IPython.display import HTML, display

# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla.copy()
cols_to_convert = ['Measurement', 'Measurement + In_Range', 'Measurement + Hypo_Hyper', 
                   'Measurement + Hour_of_day', 'Measurement + Day_of_week', 'Todas combinaciones']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
new_tabla1 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    new_tabla1 = pd.concat([new_tabla1, pd.DataFrame([row])])

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>MEJOR ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = new_tabla1.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

MEJOR ARQUITECTURA 1, LSTM
Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 1.654387 Measurement + In_Range
Validación MAE 0.301576 Measurement + In_Range
Test MAE 0.533755 Measurement + In_Range
Entrenamiento RMSE 6.209878 Measurement + In_Range
Validación RMSE 1.307544 Measurement + In_Range
Test RMSE 1.662802 Measurement + In_Range

Tabla Arquitectura 2, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [346]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla vacía
tabla = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement',
    'Measurement + In_Range',
    'Measurement + Hypo_Hyper',
    'Measurement + Hour_of_day',
    'Measurement + Day_of_week',
    'Todas combinaciones'
])

# Definir los valores en cada posición
tabla.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla.loc[1, 'Measurement'] = train_eval_measurement_2
tabla.loc[2, 'Measurement'] = val_eval_measurement_2
tabla.loc[3, 'Measurement'] = test_eval_measurement_2

tabla.loc[4, 'Measurement'] = train_rmse_measurement_2
tabla.loc[5, 'Measurement'] = val_rmse_measurement_2
tabla.loc[6, 'Measurement'] = test_rmse_measurement_2

tabla.loc[1, 'Measurement + In_Range'] = train_eval_measurement_in_range_2
tabla.loc[2, 'Measurement + In_Range'] = val_eval_measurement_in_range_2
tabla.loc[3, 'Measurement + In_Range'] = test_eval_measurement_in_range_2

tabla.loc[4, 'Measurement + In_Range'] = train_rmse_measurement_in_range_2
tabla.loc[5, 'Measurement + In_Range'] = val_rmse_measurement_in_range_2
tabla.loc[6, 'Measurement + In_Range'] = test_rmse_measurement_in_range_2

tabla.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2
tabla.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2
tabla.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2

tabla.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2
tabla.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2
tabla.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2

tabla.loc[1, 'Measurement + Hour_of_day'] = train_eval_measurement_hour_of_day_2
tabla.loc[2, 'Measurement + Hour_of_day'] = val_eval_measurement_hour_of_day_2
tabla.loc[3, 'Measurement + Hour_of_day'] = test_eval_measurement_hour_of_day_2

tabla.loc[4, 'Measurement + Hour_of_day'] = train_rmse_measurement_hour_of_day_2
tabla.loc[5, 'Measurement + Hour_of_day'] = val_rmse_measurement_hour_of_day_2
tabla.loc[6, 'Measurement + Hour_of_day'] = test_rmse_measurement_hour_of_day_2

tabla.loc[1, 'Measurement + Day_of_week'] = train_eval_measurement_day_of_week_2
tabla.loc[2, 'Measurement + Day_of_week'] = val_eval_measurement_day_of_week_2
tabla.loc[3, 'Measurement + Day_of_week'] = test_eval_measurement_day_of_week_2

tabla.loc[4, 'Measurement + Day_of_week'] = train_rmse_measurement_day_of_week_2
tabla.loc[5, 'Measurement + Day_of_week'] = val_rmse_measurement_day_of_week_2
tabla.loc[6, 'Measurement + Day_of_week'] = test_rmse_measurement_day_of_week_2

tabla.loc[1, 'Todas combinaciones'] = train_eval_measurement_combined_2
tabla.loc[2, 'Todas combinaciones'] = val_eval_measurement_combined_2
tabla.loc[3, 'Todas combinaciones'] = test_eval_measurement_combined_2

tabla.loc[4, 'Todas combinaciones'] = train_rmse_measurement_combined_2
tabla.loc[5, 'Todas combinaciones'] = val_rmse_measurement_combined_2
tabla.loc[6, 'Todas combinaciones'] = test_rmse_measurement_combined_2

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child)', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 2, LSTM
Evaluación+Métrica Measurement Measurement + In_Range Measurement + Hypo_Hyper Measurement + Hour_of_day Measurement + Day_of_week Todas combinaciones
Entrenamiento MAE 1.675128 1.232212 1.083968 1.998371 1.669242 2.289662
Validación MAE 0.905134 0.556094 0.271668 1.057032 0.704422 0.824245
Test MAE 0.755180 0.752238 0.231488 1.351160 0.774629 0.658146
Entrenamiento RMSE 4.604982 4.563739 4.527636 5.443112 5.377539 6.420724
Validación RMSE 1.474888 1.279399 1.200419 1.727100 1.490385 1.827995
Test RMSE 1.422599 1.383229 1.222260 1.923753 1.552870 1.839697
In [347]:
import pandas as pd
from IPython.display import HTML, display

# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla.copy()
cols_to_convert = ['Measurement', 'Measurement + In_Range', 'Measurement + Hypo_Hyper', 
                   'Measurement + Hour_of_day', 'Measurement + Day_of_week', 'Todas combinaciones']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
new_tabla2 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    new_tabla2 = pd.concat([new_tabla2, pd.DataFrame([row])])

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>MEJOR ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = new_tabla2.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

MEJOR ARQUITECTURA 2, LSTM
Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 1.083968 Measurement + Hypo_Hyper
Validación MAE 0.271668 Measurement + Hypo_Hyper
Test MAE 0.231488 Measurement + Hypo_Hyper
Entrenamiento RMSE 4.527636 Measurement + Hypo_Hyper
Validación RMSE 1.200419 Measurement + Hypo_Hyper
Test RMSE 1.222260 Measurement + Hypo_Hyper

Tabla Arquitectura 3, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • return_sequences: True.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5.
  • units: 64. Número de neuronas en la capa LSTM.
  • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [348]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla vacía
tabla = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement',
    'Measurement + In_Range',
    'Measurement + Hypo_Hyper',
    'Measurement + Hour_of_day',
    'Measurement + Day_of_week',
    'Todas combinaciones'
])

# Definir los valores en cada posición
tabla.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla.loc[1, 'Measurement'] = train_eval_measurement_3
tabla.loc[2, 'Measurement'] = val_eval_measurement_3
tabla.loc[3, 'Measurement'] = test_eval_measurement_3

tabla.loc[4, 'Measurement'] = train_rmse_measurement_3
tabla.loc[5, 'Measurement'] = val_rmse_measurement_3
tabla.loc[6, 'Measurement'] = test_rmse_measurement_3

tabla.loc[1, 'Measurement + In_Range'] = train_eval_measurement_in_range_3
tabla.loc[2, 'Measurement + In_Range'] = val_eval_measurement_in_range_3
tabla.loc[3, 'Measurement + In_Range'] = test_eval_measurement_in_range_3

tabla.loc[4, 'Measurement + In_Range'] = train_rmse_measurement_in_range_3
tabla.loc[5, 'Measurement + In_Range'] = val_rmse_measurement_in_range_3
tabla.loc[6, 'Measurement + In_Range'] = test_rmse_measurement_in_range_3

tabla.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3
tabla.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3
tabla.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3

tabla.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3
tabla.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3
tabla.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3

tabla.loc[1, 'Measurement + Hour_of_day'] = train_eval_measurement_hour_of_day_3
tabla.loc[2, 'Measurement + Hour_of_day'] = val_eval_measurement_hour_of_day_3
tabla.loc[3, 'Measurement + Hour_of_day'] = test_eval_measurement_hour_of_day_3

tabla.loc[4, 'Measurement + Hour_of_day'] = train_rmse_measurement_hour_of_day_3
tabla.loc[5, 'Measurement + Hour_of_day'] = val_rmse_measurement_hour_of_day_3
tabla.loc[6, 'Measurement + Hour_of_day'] = test_rmse_measurement_hour_of_day_3

tabla.loc[1, 'Measurement + Day_of_week'] = train_eval_measurement_day_of_week_3
tabla.loc[2, 'Measurement + Day_of_week'] = val_eval_measurement_day_of_week_3
tabla.loc[3, 'Measurement + Day_of_week'] = test_eval_measurement_day_of_week_3

tabla.loc[4, 'Measurement + Day_of_week'] = train_rmse_measurement_day_of_week_3
tabla.loc[5, 'Measurement + Day_of_week'] = val_rmse_measurement_day_of_week_3
tabla.loc[6, 'Measurement + Day_of_week'] = test_rmse_measurement_day_of_week_3

tabla.loc[1, 'Todas combinaciones'] = train_eval_measurement_combined_3
tabla.loc[2, 'Todas combinaciones'] = val_eval_measurement_combined_3
tabla.loc[3, 'Todas combinaciones'] = test_eval_measurement_combined_3

tabla.loc[4, 'Todas combinaciones'] = train_rmse_measurement_combined_3
tabla.loc[5, 'Todas combinaciones'] = val_rmse_measurement_combined_3
tabla.loc[6, 'Todas combinaciones'] = test_rmse_measurement_combined_3

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child)', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, LSTM
Evaluación+Métrica Measurement Measurement + In_Range Measurement + Hypo_Hyper Measurement + Hour_of_day Measurement + Day_of_week Todas combinaciones
Entrenamiento MAE 12.524332 7.778633 7.922630 5.086625 10.655787 12.632179
Validación MAE 12.698191 4.523032 4.220335 3.953882 7.297052 6.946190
Test MAE 18.400576 9.269299 6.241282 6.964336 13.426828 12.303682
Entrenamiento RMSE 15.802562 10.855287 10.941482 7.240564 15.592890 26.462608
Validación RMSE 13.540171 5.989138 5.068966 4.714972 8.962604 8.469077
Test RMSE 19.128107 10.361228 6.475054 7.407691 14.679519 13.664134
In [349]:
import pandas as pd
from IPython.display import HTML, display

# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla.copy()
cols_to_convert = ['Measurement', 'Measurement + In_Range', 'Measurement + Hypo_Hyper', 
                   'Measurement + Hour_of_day', 'Measurement + Day_of_week', 'Todas combinaciones']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
new_tabla3 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    new_tabla3 = pd.concat([new_tabla3, pd.DataFrame([row])])

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>MEJOR ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = new_tabla3.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")

styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

MEJOR ARQUITECTURA 3, LSTM
Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 5.086625 Measurement + Hour_of_day
Validación MAE 3.953882 Measurement + Hour_of_day
Test MAE 6.241282 Measurement + Hypo_Hyper
Entrenamiento RMSE 7.240564 Measurement + Hour_of_day
Validación RMSE 4.714972 Measurement + Hour_of_day
Test RMSE 6.475054 Measurement + Hypo_Hyper

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM¶

In [350]:
# Combina las tablas horizontalmente
combined_table = pd.concat([new_tabla1, new_tabla2, new_tabla3], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 1.654387 Measurement + In_Range Entrenamiento MAE 1.083968 Measurement + Hypo_Hyper Entrenamiento MAE 5.086625 Measurement + Hour_of_day
Validación MAE 0.301576 Measurement + In_Range Validación MAE 0.271668 Measurement + Hypo_Hyper Validación MAE 3.953882 Measurement + Hour_of_day
Test MAE 0.533755 Measurement + In_Range Test MAE 0.231488 Measurement + Hypo_Hyper Test MAE 6.241282 Measurement + Hypo_Hyper
Entrenamiento RMSE 6.209878 Measurement + In_Range Entrenamiento RMSE 4.527636 Measurement + Hypo_Hyper Entrenamiento RMSE 7.240564 Measurement + Hour_of_day
Validación RMSE 1.307544 Measurement + In_Range Validación RMSE 1.200419 Measurement + Hypo_Hyper Validación RMSE 4.714972 Measurement + Hour_of_day
Test RMSE 1.662802 Measurement + In_Range Test RMSE 1.222260 Measurement + Hypo_Hyper Test RMSE 6.475054 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, LSTM¶

In [351]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = new_tabla1.loc[new_tabla1['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not new_tabla1.loc[new_tabla1['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = new_tabla1.loc[new_tabla1['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = new_tabla2.loc[new_tabla2['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not new_tabla2.loc[new_tabla2['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = new_tabla2.loc[new_tabla2['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = new_tabla3.loc[new_tabla3['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not new_tabla3.loc[new_tabla3['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = new_tabla3.loc[new_tabla3['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
    {'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_22 = styled_result.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_22}</div>'))

RESULTADO A MEJOR ARQUITECTURA, LSTM

ARQUITECTURA 2
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 1.083968 Measurement + Hypo_Hyper 2
Validación MAE 0.271668 Measurement + Hypo_Hyper 2
Test MAE 0.231488 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 4.527636 Measurement + Hypo_Hyper 2
Validación RMSE 1.200419 Measurement + Hypo_Hyper 2
Test RMSE 1.222260 Measurement + Hypo_Hyper 2

Conclusión LSTM¶

La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.

La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:

  1. Puerta de entrada (input gate): Determina qué nueva información debe agregarse al estado actual.
  2. Puerta de olvido (forget gate): Determina qué información antigua debe descartarse del estado actual.
  3. Puerta de salida (output gate): Determina qué parte del estado actual debe emitirse como salida. Además, las puertas contienen funciones de activación.

El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.

En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados e incluso se puede apreciar que las características de entrada con mejores resultados son Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 2:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos.
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Validación RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjunto de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Entrenamiento MAE, Validación MAE, Test MAE, Entrenamiento RMSE, Validación RMSE y Test RMSE, estas evaluaciones determinan que es capaz de realizar un alto entrenamiento, alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2, utilizando las características de entrada Measurement + Hypo_Hyper.

Este análisis de resultados concluye que los próximos análisis se hagan con las características de entrada Measurement + Hypo_Hyper ya que han obtenido globalmente unos mejores resultados en rendimiento de predicción y precisión al entrenamiento y generalización a nuevos datos.

Redes Neuronales Recurrentes Convolucionales (CRNN)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo CRNN con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.¶

En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hyperglycemia" indica si una medición está por encima del rango normal.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal

Mejor arquitectura 3¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [353]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [354]:
df
Out[354]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-11-06 06:45:00 134 False False
2018-11-06 07:00:00 134 False False
2018-11-06 07:15:00 139 False False
2018-11-06 07:30:00 145 False False
2018-11-06 07:45:00 147 False False
... ... ... ...
2022-03-08 11:30:00 179 False False
2022-03-08 12:30:00 173 False False
2022-03-08 12:45:00 171 False False
2022-03-08 13:15:00 165 False False
2022-03-15 23:46:00 155 False False

10409 rows × 3 columns

2. Dividir el conjunto de datos¶

In [355]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [356]:
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_48"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv1d_15 (Conv1D)          (None, 6, 32)             320       
                                                                 
 batch_normalization_30 (Bat  (None, 6, 32)            128       
 chNormalization)                                                
                                                                 
 max_pooling1d_15 (MaxPoolin  (None, 3, 32)            0         
 g1D)                                                            
                                                                 
 dropout_30 (Dropout)        (None, 3, 32)             0         
                                                                 
 lstm_58 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_31 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_31 (Dropout)        (None, 64)                0         
                                                                 
 dense_48 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________

4. Entrenar el modelo¶

In [357]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1471/1471 [==============================] - 4s 2ms/step - loss: 48.2772 - val_loss: 23.9773
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 22.6054 - val_loss: 10.5451
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 21.9258 - val_loss: 8.8166
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 21.5737 - val_loss: 6.1133
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 21.1827 - val_loss: 12.7950
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 21.0718 - val_loss: 12.1782
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 21.0374 - val_loss: 6.8255

5. Evaluar¶

In [358]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN = train_eval
val_eval_measurement_hypo_hype_3_CRNN = val_eval
test_eval_measurement_hypo_hype_3_CRNN = test_eval

train_rmse_measurement_hypo_hype_3_CRNN = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN = test_rmse
2942/2942 [==============================] - 2s 649us/step
368/368 [==============================] - 0s 662us/step
368/368 [==============================] - 0s 659us/step
Evaluación Entrenamiento MAE: 11.181082725524902
Evaluación Validación MAE: 6.825511455535889
Evaluación Prueba MAE: 7.6780290603637695
Entrenamiento RMSE: 17.095659255981445
Validación RMSE: 8.660697937011719
Prueba RMSE: 11.64884090423584

6. Gráfica¶

In [359]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [360]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 9ms/step
1/1 [==============================] - 0s 9ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-16 00:01:00           NaN       166.588135
2022-03-16 00:16:00           NaN       164.287796

Distintas arquitecturas CRNN probadas¶

1º Optimización de hiperparámetros¶

In [146]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_2_CRNN = train_eval
val_eval_measurement_hypo_hype_2_CRNN = val_eval
test_eval_measurement_hypo_hype_2_CRNN = test_eval

train_rmse_measurement_hypo_hype_2_CRNN = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1471/1471 [==============================] - 3s 2ms/step - loss: 136.9881 - val_loss: 126.6721
Epoch 2/20
1471/1471 [==============================] - 2s 1ms/step - loss: 108.5541 - val_loss: 99.0082
Epoch 3/20
1471/1471 [==============================] - 2s 1ms/step - loss: 81.0333 - val_loss: 71.5418
Epoch 4/20
1471/1471 [==============================] - 2s 2ms/step - loss: 53.8995 - val_loss: 44.2691
Epoch 5/20
1471/1471 [==============================] - 2s 1ms/step - loss: 29.1893 - val_loss: 19.4732
Epoch 6/20
1471/1471 [==============================] - 2s 1ms/step - loss: 19.0227 - val_loss: 12.2275
Epoch 7/20
1471/1471 [==============================] - 2s 1ms/step - loss: 17.8867 - val_loss: 12.0720
Epoch 8/20
1471/1471 [==============================] - 2s 1ms/step - loss: 17.8813 - val_loss: 12.3091
Epoch 9/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.2959 - val_loss: 9.2642
Epoch 10/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.0885 - val_loss: 8.9684
Epoch 11/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.0371 - val_loss: 8.3480
Epoch 12/20
1471/1471 [==============================] - 2s 2ms/step - loss: 16.0685 - val_loss: 8.3343
Epoch 13/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.0704 - val_loss: 8.3280
Epoch 14/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.0706 - val_loss: 8.3287
Epoch 15/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.0709 - val_loss: 8.3294
Epoch 16/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.0706 - val_loss: 8.3301
2942/2942 [==============================] - 2s 536us/step
368/368 [==============================] - 0s 536us/step
368/368 [==============================] - 0s 550us/step
Evaluación Entrenamiento MAE: 16.1252498626709
Evaluación Validación MAE: 8.330071449279785
Evaluación Prueba MAE: 17.305015563964844
Entrenamiento RMSE: 22.40139389038086
Validación RMSE: 11.44087028503418
Prueba RMSE: 19.776081085205078

2º Optimización de hiperparámetros¶

In [147]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_1_CRNN = train_eval
val_eval_measurement_hypo_hype_1_CRNN = val_eval
test_eval_measurement_hypo_hype_1_CRNN = test_eval

train_rmse_measurement_hypo_hype_1_CRNN = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1471/1471 [==============================] - 3s 1ms/step - loss: 135.6173 - val_loss: 124.1344
Epoch 2/20
1471/1471 [==============================] - 2s 1ms/step - loss: 104.4509 - val_loss: 93.3071
Epoch 3/20
1471/1471 [==============================] - 2s 1ms/step - loss: 73.7337 - val_loss: 62.6001
Epoch 4/20
1471/1471 [==============================] - 2s 1ms/step - loss: 43.7654 - val_loss: 32.3940
Epoch 5/20
1471/1471 [==============================] - 2s 1ms/step - loss: 20.9529 - val_loss: 12.1744
Epoch 6/20
1471/1471 [==============================] - 2s 1ms/step - loss: 18.1850 - val_loss: 11.2732
Epoch 7/20
1471/1471 [==============================] - 2s 1ms/step - loss: 17.2536 - val_loss: 10.1044
Epoch 8/20
1471/1471 [==============================] - 2s 1ms/step - loss: 17.5440 - val_loss: 10.9213
Epoch 9/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.7779 - val_loss: 9.7612
Epoch 10/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.0673 - val_loss: 8.4114
Epoch 11/20
1471/1471 [==============================] - 2s 1ms/step - loss: 17.7470 - val_loss: 11.4089
Epoch 12/20
1471/1471 [==============================] - 2s 1ms/step - loss: 16.6330 - val_loss: 9.7207
Epoch 13/20
1471/1471 [==============================] - 2s 1ms/step - loss: 17.3310 - val_loss: 10.8876
2942/2942 [==============================] - 2s 510us/step
368/368 [==============================] - 0s 555us/step
368/368 [==============================] - 0s 532us/step
Evaluación Entrenamiento MAE: 16.346696853637695
Evaluación Validación MAE: 10.887613296508789
Evaluación Prueba MAE: 21.2574462890625
Entrenamiento RMSE: 22.930130004882812
Validación RMSE: 13.952215194702148
Prueba RMSE: 23.73246955871582

Resultado obtenido con 3 arquitecturas distintas de la CRNN¶

Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement' + Hypoglycemia + Hyperglycemia

El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.

Las arquitecturas CRNN utilizadas son:

  • Arquitectura 1(hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 64. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reúne todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [371]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_1_CRNN vacía
tabla_1_CRNN = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_1_CRNN.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_1_CRNN.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_1_CRNN.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN
tabla_1_CRNN.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN
tabla_1_CRNN.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN

tabla_1_CRNN.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN
tabla_1_CRNN.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN
tabla_1_CRNN.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(2) {
        text-align: center;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 16.346697
Validación MAE 10.887613
Test MAE 21.257446
Entrenamiento RMSE 22.930130
Validación RMSE 13.952215
Test RMSE 23.732470
In [372]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_1_CRNN = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_1_CRNN = pd.concat([tabla_1_CRNN, pd.DataFrame([row])])

Tabla Arquitectura 2, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 64. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [373]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_2_CRNN vacía
tabla_2_CRNN = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_2_CRNN.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_2_CRNN.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_2_CRNN.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN
tabla_2_CRNN.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN
tabla_2_CRNN.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN

tabla_2_CRNN.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN
tabla_2_CRNN.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN
tabla_2_CRNN.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 2, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 16.125250
Validación MAE 8.330071
Test MAE 17.305016
Entrenamiento RMSE 22.401394
Validación RMSE 11.440870
Test RMSE 19.776081
In [374]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_2_CRNN = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_2_CRNN = pd.concat([tabla_2_CRNN, pd.DataFrame([row])])

Tabla Arquitectura 3, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [375]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_CRNN vacía
tabla_3_CRNN = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_CRNN.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_CRNN.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_CRNN.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN
tabla_3_CRNN.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN
tabla_3_CRNN.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN

tabla_3_CRNN.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN
tabla_3_CRNN.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN
tabla_3_CRNN.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 11.181083
Validación MAE 6.825511
Test MAE 7.678029
Entrenamiento RMSE 17.095659
Validación RMSE 8.660698
Test RMSE 11.648841
In [376]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_3_CRNN = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_3_CRNN = pd.concat([tabla_3_CRNN, pd.DataFrame([row])])

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN¶

In [377]:
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN, tabla_2_CRNN, tabla_3_CRNN], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 16.346697 Measurement + Hypo_Hyper Entrenamiento MAE 16.125250 Measurement + Hypo_Hyper Entrenamiento MAE 11.181083 Measurement + Hypo_Hyper
Validación MAE 10.887613 Measurement + Hypo_Hyper Validación MAE 8.330071 Measurement + Hypo_Hyper Validación MAE 6.825511 Measurement + Hypo_Hyper
Test MAE 21.257446 Measurement + Hypo_Hyper Test MAE 17.305016 Measurement + Hypo_Hyper Test MAE 7.678029 Measurement + Hypo_Hyper
Entrenamiento RMSE 22.930130 Measurement + Hypo_Hyper Entrenamiento RMSE 22.401394 Measurement + Hypo_Hyper Entrenamiento RMSE 17.095659 Measurement + Hypo_Hyper
Validación RMSE 13.952215 Measurement + Hypo_Hyper Validación RMSE 11.440870 Measurement + Hypo_Hyper Validación RMSE 8.660698 Measurement + Hypo_Hyper
Test RMSE 23.732470 Measurement + Hypo_Hyper Test RMSE 19.776081 Measurement + Hypo_Hyper Test RMSE 11.648841 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, CRNN¶

In [378]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = tabla_1_CRNN.loc[tabla_1_CRNN['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_1_CRNN.loc[tabla_1_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = tabla_1_CRNN.loc[tabla_1_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = tabla_2_CRNN.loc[tabla_2_CRNN['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_2_CRNN.loc[tabla_2_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = tabla_2_CRNN.loc[tabla_2_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = tabla_3_CRNN.loc[tabla_3_CRNN['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_3_CRNN.loc[tabla_3_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = tabla_3_CRNN.loc[tabla_3_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
    {'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
CRNN_22 = styled_result.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_22}</div>'))

RESULTADO A MEJOR ARQUITECTURA, CRNN

ARQUITECTURA 3
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 11.181083 Measurement + Hypo_Hyper 3
Validación MAE 6.825511 Measurement + Hypo_Hyper 3
Test MAE 7.678029 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 17.095659 Measurement + Hypo_Hyper 3
Validación RMSE 8.660698 Measurement + Hypo_Hyper 3
Test RMSE 11.648841 Measurement + Hypo_Hyper 3

Conclusión CRNN¶

La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.

Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.

La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.

En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 3:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos.
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Validación RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjunto de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Entrenamiento MAE, Validación MAE, Test MAE, Entrenamiento RMSE, Validación RMSE y Test RMSE, estas evaluaciones determinan que es capaz de realizar un alto entrenamiento, alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.

Comparativa de resultados LSTM y CRNN¶

In [369]:
from IPython.display import display, HTML

html_tables = f"""
<style>
    .table-wrapper {{
        display: inline-block;
        font-size: 14px;
        vertical-align: top;
        margin-right: 20px;
    }}
    .table-wrapper table {{
        font-size: 14px;
    }}
    .table-wrapper th, .table-wrapper td {{
        width: 100px;
        text-align: center;
    }}
    .table-wrapper h3 {{
        text-align: center;
        font-size: 18px;
        color: blue;
    }}
    h2 {{
        text-align: center;
    }}
</style>
<h2>Paciente 22</h2>
<div class="table-wrapper">
    <h3>LSTM</h3>
    {LSTM_22}
</div>
<div class="table-wrapper">
    <h3>CRNN</h3>
    {CRNN_22}
</div>
"""

display(HTML(html_tables))

Paciente 22

LSTM

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 1.083968 Measurement + Hypo_Hyper 2
Validación MAE 0.271668 Measurement + Hypo_Hyper 2
Test MAE 0.231488 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 4.527636 Measurement + Hypo_Hyper 2
Validación RMSE 1.200419 Measurement + Hypo_Hyper 2
Test RMSE 1.222260 Measurement + Hypo_Hyper 2

CRNN

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 11.181083 Measurement + Hypo_Hyper 3
Validación MAE 6.825511 Measurement + Hypo_Hyper 3
Test MAE 7.678029 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 17.095659 Measurement + Hypo_Hyper 3
Validación RMSE 8.660698 Measurement + Hypo_Hyper 3
Test RMSE 11.648841 Measurement + Hypo_Hyper 3

Mejor Modelo (Paciente 22)¶

In [379]:
from IPython.display import display, HTML

# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN': 1, 'tabla_2_CRNN': 2, 'tabla_3_CRNN': 3}
tables_arch2 = {'new_tabla1': 1, 'new_tabla2': 2, 'new_tabla3': 3}

# Para cada fila
for row in rows:
    min_vals = []
    
    # Arquitectura 1
    for table_name, arch in tables_arch1.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'CRNN', arch))

    # Arquitectura 2
    for table_name, arch in tables_arch2.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'LSTM', arch))

    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])

    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')

    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [min_arch],
        'Arquitectura (Hiperparámetros)': [min_num]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 22)</span></center>"
display(HTML(html_text))

# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()

# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))

# Nombrando
styled_result1 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result1 = styled_result1.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_22.style.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"

# Muestra el HTML
display(HTML(html))

# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))


# Mostrar la figura
display(fig_hypo_hype_2_LSTM_22)

# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_22.style.set_properties(**{'text-align': 'center'}).hide(axis='index')

# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"

# Mostrar la tabla centrada
display(HTML(table_html))

MEJOR ARQUITECTURA (Paciente 22)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 1.083968 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.271668 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.231488 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 4.527636 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 1.200419 Measurement + Hypo_Hyper LSTM 2
Test RMSE 1.222260 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-03-16 00:01:00 nan 156.141495
2022-03-16 00:16:00 nan 157.410568

Precisión del modelo con Clarke Error Grid

Zona Conteo Proporción
A 11758 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Conclusión Paciente 22¶

Los resultados nos aclara que la mejor opción para el paciente 22 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.

Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

La conclusión final para el paciente 22 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hour_of_day para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.

Paciente 24
¶

La comparativa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 24 debido a que tras un análisis extenso se ha comprobado que es el 1º con mayor número de mediciones.

Se va a llevar a cabo la comparativa de soluciones con los modelos RNN y CRNN pero con la mejor solución ya obtenida con el paciente 22. De esta manera se va a trabajar con la mejor arquitectura de hiperparámetros y de características de entrada así se va a poder confirmar que el experimento se ha realizado con éxito.

Las 3 arquitecturas de hiperparámetros de LSTM y CRNN son:

Arquitectura de hiperparámetros de LSTM:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 64. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • return_sequences: True.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5.
    • units: 64. Número de neuronas en la capa LSTM.
    • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Arquitectura de hiperparámetros de CRNN:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

La característica de entrada va a ser: 'Measurement + Hypo_Hyper'

In [158]:
# Filtrar los datos para el paciente 24
df_paciente_24 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 24]

# Imprimir los resultados
df_paciente_24
Out[158]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-07-13 10:15:00 24 2018-07-13 1900-01-01 10:15:00 142 False True False 10 4
2018-07-13 10:30:00 24 2018-07-13 1900-01-01 10:30:00 142 True False False 10 4
2018-07-13 10:45:00 24 2018-07-13 1900-01-01 10:45:00 153 True False False 10 4
2018-07-13 11:00:00 24 2018-07-13 1900-01-01 11:00:00 160 True False False 11 4
2018-07-13 11:15:00 24 2018-07-13 1900-01-01 11:15:00 162 False False True 11 4
... ... ... ... ... ... ... ... ... ...
2022-03-12 05:30:00 24 2022-03-12 1900-01-01 05:30:00 156 True False False 5 5
2022-03-12 06:30:00 24 2022-03-12 1900-01-01 06:30:00 154 True False False 6 5
2022-03-12 06:45:00 24 2022-03-12 1900-01-01 06:45:00 154 True False False 6 5
2022-03-12 07:45:00 24 2022-03-12 1900-01-01 07:45:00 153 True False False 7 5
2022-03-12 08:30:00 24 2022-03-12 1900-01-01 08:30:00 158 False True False 8 5

11987 rows × 9 columns

Red Neuronal Recurrente (RNN) con el algoritmo Long Short-Term Memory (LSTM)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo LSTM con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'¶

En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal
  • "Hyperglycemia" indica si una medición está por encima del rango normal.

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [159]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [160]:
df
Out[160]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-07-13 10:15:00 142 True False
2018-07-13 10:30:00 142 False False
2018-07-13 10:45:00 153 False False
2018-07-13 11:00:00 160 False False
2018-07-13 11:15:00 162 False True
... ... ... ...
2022-03-12 05:30:00 156 False False
2022-03-12 06:30:00 154 False False
2022-03-12 06:45:00 154 False False
2022-03-12 07:45:00 153 False False
2022-03-12 08:30:00 158 True False

11987 rows × 3 columns

2. Dividir el conjunto de datos¶

In [161]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [162]:
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_21"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_27 (LSTM)              (None, 64)                17408     
                                                                 
 dense_21 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [163]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1606/1606 [==============================] - 5s 3ms/step - loss: 3.3317 - val_loss: 0.7470
Epoch 2/20
1606/1606 [==============================] - 4s 3ms/step - loss: 2.4871 - val_loss: 1.6190
Epoch 3/20
1606/1606 [==============================] - 4s 3ms/step - loss: 2.0046 - val_loss: 0.1555
Epoch 4/20
1606/1606 [==============================] - 4s 3ms/step - loss: 2.0574 - val_loss: 0.4030
Epoch 5/20
1606/1606 [==============================] - 4s 3ms/step - loss: 1.9047 - val_loss: 1.4204
Epoch 6/20
1606/1606 [==============================] - 4s 3ms/step - loss: 1.8448 - val_loss: 0.7944

5. Evaluar¶

In [164]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2_LSTM_24 = train_eval
val_eval_measurement_hypo_hype_2_LSTM_24 = val_eval
test_eval_measurement_hypo_hype_2_LSTM_24 = test_eval

train_rmse_measurement_hypo_hype_2_LSTM_24 = train_rmse
val_rmse_measurement_hypo_hype_2_LSTM_24 = val_rmse
test_rmse_measurement_hypo_hype_2_LSTM_24 = test_rmse
3211/3211 [==============================] - 3s 799us/step
402/402 [==============================] - 0s 802us/step
402/402 [==============================] - 0s 798us/step
Evaluación Entrenamiento MAE: 1.848578929901123
Evaluación Validación MAE: 0.7943907380104065
Evaluación Prueba MAE: 0.8569515347480774
Entrenamiento RMSE: 5.513991832733154
Validación RMSE: 0.9274844527244568
Prueba RMSE: 0.9750375151634216

6. Gráfica¶

In [165]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [166]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')

# Almacenar dato
future_predictions_hypo_hype_2_LSTM_24 = future_predictions

plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-12 08:45:00           NaN       156.134811
2022-03-12 09:00:00           NaN       155.769943

6.2 Clarke Error Grid¶

In [167]:
def clarke_error_grid(ref_values, pred_values):
    assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))

    if max(ref_values) > 400 or max(pred_values) > 400:
        print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
    if min(ref_values) < 0 or min(pred_values) < 0:
        print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values),  min(pred_values)))

    plt.clf()
    plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
    plt.title("Clarke Error Grid")
    plt.xlabel("Reference Concentration (mg/dl)")
    plt.ylabel("Prediction Concentration (mg/dl)")
    plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.gca().set_facecolor('white')
    plt.gca().set_xlim([0, 400])
    plt.gca().set_ylim([0, 400])
    plt.gca().set_aspect((400)/(400))
    plt.plot([0, 400], [0, 400], ':', c='black')
    plt.plot([0, 175/3], [70, 70], '-', c='black')
    plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
    plt.plot([70, 70], [84, 400],'-', c='black')
    plt.plot([0, 70], [180, 180], '-', c='black')
    plt.plot([70, 290], [180, 400],'-', c='black')
    plt.plot([70, 70], [0, 56], '-', c='black')
    plt.plot([70, 400], [56, 320],'-', c='black')
    plt.plot([180, 180], [0, 70], '-', c='black')
    plt.plot([180, 400], [70, 70], '-', c='black')
    plt.plot([240, 240], [70, 180],'-', c='black')
    plt.plot([240, 400], [180, 180], '-', c='black')
    plt.plot([130, 180], [0, 70], '-', c='black')
    plt.text(30, 15, "A", fontsize=15)
    plt.text(370, 260, "B", fontsize=15)
    plt.text(280, 370, "B", fontsize=15)
    plt.text(160, 370, "C", fontsize=15)
    plt.text(160, 15, "C", fontsize=15)
    plt.text(30, 140, "D", fontsize=15)
    plt.text(370, 120, "D", fontsize=15)
    plt.text(30, 370, "E", fontsize=15)
    plt.text(370, 15, "E", fontsize=15)

    zone = [0] * 5
    for i in range(len(ref_values)):
        if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
            zone[0] += 1  # Zone A
        elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
            zone[4] += 1  # Zone E
        elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
            zone[2] += 1  # Zone C
        elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
            zone[3] += 1  # Zone D
        else:
            zone[1] += 1  # Zone B

    return plt, zone

ref_values = y_test
pred_values = y_pred_test

plt, zone = clarke_error_grid(ref_values, pred_values)

# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_24 = plt.gcf()

# Mostrar la figura
plt.show()

# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})

# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total

# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))

# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_24 = zone_df

# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_24.style.hide(axis="index"))
Conteo de Zonas:
Zona Conteo Proporción
A 12836 100.00%
B 0 0.00%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [168]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1_LSTM_24 = train_eval
val_eval_measurement_hypo_hype_1_LSTM_24 = val_eval
test_eval_measurement_hypo_hype_1_LSTM_24 = test_eval

train_rmse_measurement_hypo_hype_1_LSTM_24 = train_rmse
val_rmse_measurement_hypo_hype_1_LSTM_24 = val_rmse
test_rmse_measurement_hypo_hype_1_LSTM_24 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_22"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_28 (LSTM)              (None, 32)                4608      
                                                                 
 dense_22 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1606/1606 [==============================] - 4s 2ms/step - loss: 136.3816 - val_loss: 124.1663
Epoch 2/20
1606/1606 [==============================] - 3s 2ms/step - loss: 107.1916 - val_loss: 95.4709
Epoch 3/20
1606/1606 [==============================] - 3s 2ms/step - loss: 78.9571 - val_loss: 67.9738
Epoch 4/20
1606/1606 [==============================] - 3s 2ms/step - loss: 52.3246 - val_loss: 41.1366
Epoch 5/20
1606/1606 [==============================] - 3s 2ms/step - loss: 29.9364 - val_loss: 18.8177
Epoch 6/20
1606/1606 [==============================] - 3s 2ms/step - loss: 17.2856 - val_loss: 11.8577
Epoch 7/20
1606/1606 [==============================] - 3s 2ms/step - loss: 10.7866 - val_loss: 6.1048
Epoch 8/20
1606/1606 [==============================] - 3s 2ms/step - loss: 7.9177 - val_loss: 4.4213
Epoch 9/20
1606/1606 [==============================] - 3s 2ms/step - loss: 6.0058 - val_loss: 2.8679
Epoch 10/20
1606/1606 [==============================] - 3s 2ms/step - loss: 4.8831 - val_loss: 1.9169
Epoch 11/20
1606/1606 [==============================] - 3s 2ms/step - loss: 4.3533 - val_loss: 0.8242
Epoch 12/20
1606/1606 [==============================] - 3s 2ms/step - loss: 3.7928 - val_loss: 1.0000
Epoch 13/20
1606/1606 [==============================] - 3s 2ms/step - loss: 3.6041 - val_loss: 0.6903
Epoch 14/20
1606/1606 [==============================] - 3s 2ms/step - loss: 3.1577 - val_loss: 0.3456
Epoch 15/20
1606/1606 [==============================] - 3s 2ms/step - loss: 2.9796 - val_loss: 0.2120
Epoch 16/20
1606/1606 [==============================] - 3s 2ms/step - loss: 2.6932 - val_loss: 0.8564
Epoch 17/20
1606/1606 [==============================] - 3s 2ms/step - loss: 2.6843 - val_loss: 0.5786
Epoch 18/20
1606/1606 [==============================] - 3s 2ms/step - loss: 2.7187 - val_loss: 1.0091
3211/3211 [==============================] - 2s 602us/step
402/402 [==============================] - 0s 605us/step
402/402 [==============================] - 0s 605us/step
Evaluación Entrenamiento MAE: 2.781226634979248
Evaluación Validación MAE: 1.009082317352295
Evaluación Prueba MAE: 1.2069369554519653
Entrenamiento RMSE: 7.843711853027344
Validación RMSE: 1.1686525344848633
Prueba RMSE: 1.3281201124191284

2º Optimización de hiperparámetros¶

In [169]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
    xs = []
    ys = []
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_LSTM_24 = train_eval
val_eval_measurement_hypo_hype_3_LSTM_24 = val_eval
test_eval_measurement_hypo_hype_3_LSTM_24 = test_eval

train_rmse_measurement_hypo_hype_3_LSTM_24 = train_rmse
val_rmse_measurement_hypo_hype_3_LSTM_24 = val_rmse
test_rmse_measurement_hypo_hype_3_LSTM_24 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_23"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_29 (LSTM)              (None, 8, 32)             4608      
                                                                 
 batch_normalization_14 (Bat  (None, 8, 32)            128       
 chNormalization)                                                
                                                                 
 dropout_14 (Dropout)        (None, 8, 32)             0         
                                                                 
 lstm_30 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_15 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_15 (Dropout)        (None, 64)                0         
                                                                 
 dense_23 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1606/1606 [==============================] - 7s 4ms/step - loss: 45.9360 - val_loss: 13.4667
Epoch 2/20
1606/1606 [==============================] - 6s 4ms/step - loss: 24.6978 - val_loss: 14.5635
Epoch 3/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.8469 - val_loss: 6.0860
Epoch 4/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.8350 - val_loss: 3.7788
Epoch 5/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.3111 - val_loss: 7.8080
Epoch 6/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.2787 - val_loss: 13.5805
Epoch 7/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.1046 - val_loss: 11.7681
3211/3211 [==============================] - 4s 1ms/step
402/402 [==============================] - 0s 1ms/step
402/402 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 14.309062004089355
Evaluación Validación MAE: 11.768108367919922
Evaluación Prueba MAE: 11.139135360717773
Entrenamiento RMSE: 16.737184524536133
Validación RMSE: 11.954730033874512
Prueba RMSE: 11.243288040161133

Resultado obtenido con 3 arquitecturas distintas de la LSTM¶

Después de realizar las pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement' + Hypoglycemia + Hyperglycemia

El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.

Las arquitecturas LSTM utilizadas son:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 64. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • return_sequences: True.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5.
    • units: 64. Número de neuronas en la capa LSTM.
    • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [170]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_1_LSTM_24 vacía
tabla_1_LSTM_24 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_1_LSTM_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_LSTM_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_LSTM_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_1_LSTM_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_LSTM_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_LSTM_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_1_LSTM_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_LSTM_24
tabla_1_LSTM_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_LSTM_24
tabla_1_LSTM_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_LSTM_24

tabla_1_LSTM_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_LSTM_24
tabla_1_LSTM_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_LSTM_24
tabla_1_LSTM_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_LSTM_24

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(2) {
        text-align: center;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_LSTM_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 2.781227
Validación MAE 1.009082
Test MAE 1.206937
Entrenamiento RMSE 7.843712
Validación RMSE 1.168653
Test RMSE 1.328120
In [171]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_LSTM_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_1_LSTM_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_1_LSTM_24 = pd.concat([tabla_1_LSTM_24, pd.DataFrame([row])])

Tabla Arquitectura 2, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [172]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla vacía
tabla_2_LSTM_24 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_2_LSTM_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_LSTM_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_LSTM_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_2_LSTM_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_LSTM_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_LSTM_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_2_LSTM_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_LSTM_24
tabla_2_LSTM_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_LSTM_24
tabla_2_LSTM_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_LSTM_24

tabla_2_LSTM_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_LSTM_24
tabla_2_LSTM_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_LSTM_24
tabla_2_LSTM_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_LSTM_24

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_LSTM_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 2, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 1.848579
Validación MAE 0.794391
Test MAE 0.856952
Entrenamiento RMSE 5.513992
Validación RMSE 0.927484
Test RMSE 0.975038
In [173]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_LSTM_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_2_LSTM_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_2_LSTM_24 = pd.concat([tabla_2_LSTM_24, pd.DataFrame([row])])

Tabla Arquitectura 3, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • return_sequences: True.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5.
  • units: 64. Número de neuronas en la capa LSTM.
  • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [174]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_LSTM_11 vacía
tabla_3_LSTM_24 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_LSTM_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_LSTM_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_LSTM_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_LSTM_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_LSTM_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_LSTM_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_LSTM_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_LSTM_24
tabla_3_LSTM_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_LSTM_24
tabla_3_LSTM_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_LSTM_24

tabla_3_LSTM_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_LSTM_24
tabla_3_LSTM_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_LSTM_24
tabla_3_LSTM_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_LSTM_24

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_LSTM_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 14.309062
Validación MAE 11.768108
Test MAE 11.139135
Entrenamiento RMSE 16.737185
Validación RMSE 11.954730
Test RMSE 11.243288
In [175]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_LSTM_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_3_LSTM_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_3_LSTM_24 = pd.concat([tabla_3_LSTM_24, pd.DataFrame([row])])

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM¶

In [176]:
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_LSTM_24, tabla_2_LSTM_24, tabla_3_LSTM_24], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 2.781227 Measurement + Hypo_Hyper Entrenamiento MAE 1.848579 Measurement + Hypo_Hyper Entrenamiento MAE 14.309062 Measurement + Hypo_Hyper
Validación MAE 1.009082 Measurement + Hypo_Hyper Validación MAE 0.794391 Measurement + Hypo_Hyper Validación MAE 11.768108 Measurement + Hypo_Hyper
Test MAE 1.206937 Measurement + Hypo_Hyper Test MAE 0.856952 Measurement + Hypo_Hyper Test MAE 11.139135 Measurement + Hypo_Hyper
Entrenamiento RMSE 7.843712 Measurement + Hypo_Hyper Entrenamiento RMSE 5.513992 Measurement + Hypo_Hyper Entrenamiento RMSE 16.737185 Measurement + Hypo_Hyper
Validación RMSE 1.168653 Measurement + Hypo_Hyper Validación RMSE 0.927484 Measurement + Hypo_Hyper Validación RMSE 11.954730 Measurement + Hypo_Hyper
Test RMSE 1.328120 Measurement + Hypo_Hyper Test RMSE 0.975038 Measurement + Hypo_Hyper Test RMSE 11.243288 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, LSTM¶

In [177]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = tabla_1_LSTM_24.loc[tabla_1_LSTM_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_1_LSTM_24.loc[tabla_1_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = tabla_1_LSTM_24.loc[tabla_1_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = tabla_2_LSTM_24.loc[tabla_2_LSTM_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_2_LSTM_24.loc[tabla_2_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = tabla_2_LSTM_24.loc[tabla_2_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = tabla_3_LSTM_24.loc[tabla_3_LSTM_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_3_LSTM_24.loc[tabla_3_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = tabla_3_LSTM_24.loc[tabla_3_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
    {'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_24 = styled_result.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_24}</div>'))

RESULTADO A MEJOR ARQUITECTURA, LSTM

ARQUITECTURA 2
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 1.848579 Measurement + Hypo_Hyper 2
Validación MAE 0.794391 Measurement + Hypo_Hyper 2
Test MAE 0.856952 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 5.513992 Measurement + Hypo_Hyper 2
Validación RMSE 0.927484 Measurement + Hypo_Hyper 2
Test RMSE 0.975038 Measurement + Hypo_Hyper 2

Conclusión LSTM¶

La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.

La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:

  1. Puerta de entrada (input gate): Determina qué nueva información debe agregarse al estado actual.
  2. Puerta de olvido (forget gate): Determina qué información antigua debe descartarse del estado actual.
  3. Puerta de salida (output gate): Determina qué parte del estado actual debe emitirse como salida. Además, las puertas contienen funciones de activación.

El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.

En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 2:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • MSE Validación: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjutno de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Evaluación Entrenamiento, Evaluación Validación, Evaluación Test, MSE Entrenamiento, MSE Validación, MSE Test estas evaluaciones determinan que es capaz de realizar un alto entrenamiento, alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.

Redes Neuronales Recurrentes Convolucionales (CRNN)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo CRNN con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.¶

En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hyperglycemia" indica si una medición está por encima del rango normal.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal

Mejor arquitectura 3¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [412]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [413]:
df
Out[413]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-07-13 10:15:00 142 True False
2018-07-13 10:30:00 142 False False
2018-07-13 10:45:00 153 False False
2018-07-13 11:00:00 160 False False
2018-07-13 11:15:00 162 False True
... ... ... ...
2022-03-12 05:30:00 156 False False
2022-03-12 06:30:00 154 False False
2022-03-12 06:45:00 154 False False
2022-03-12 07:45:00 153 False False
2022-03-12 08:30:00 158 True False

11987 rows × 3 columns

2. Dividir el conjunto de datos¶

In [414]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [415]:
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_53"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv1d_20 (Conv1D)          (None, 6, 32)             320       
                                                                 
 batch_normalization_40 (Bat  (None, 6, 32)            128       
 chNormalization)                                                
                                                                 
 max_pooling1d_20 (MaxPoolin  (None, 3, 32)            0         
 g1D)                                                            
                                                                 
 dropout_40 (Dropout)        (None, 3, 32)             0         
                                                                 
 lstm_63 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_41 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_41 (Dropout)        (None, 64)                0         
                                                                 
 dense_53 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________

4. Entrenar el modelo¶

In [416]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1606/1606 [==============================] - 5s 2ms/step - loss: 44.9550 - val_loss: 24.7769
Epoch 2/20
1606/1606 [==============================] - 3s 2ms/step - loss: 24.5620 - val_loss: 15.5933
Epoch 3/20
1606/1606 [==============================] - 3s 2ms/step - loss: 24.0151 - val_loss: 6.3546
Epoch 4/20
1606/1606 [==============================] - 3s 2ms/step - loss: 23.5473 - val_loss: 5.2315
Epoch 5/20
1606/1606 [==============================] - 4s 2ms/step - loss: 23.5834 - val_loss: 7.4198
Epoch 6/20
1606/1606 [==============================] - 3s 2ms/step - loss: 23.3172 - val_loss: 8.6975
Epoch 7/20
1606/1606 [==============================] - 3s 2ms/step - loss: 23.3292 - val_loss: 9.7800

5. Evaluar¶

In [417]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN_24 = train_eval
val_eval_measurement_hypo_hype_3_CRNN_24 = val_eval
test_eval_measurement_hypo_hype_3_CRNN_24 = test_eval

train_rmse_measurement_hypo_hype_3_CRNN_24 = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN_24 = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN_24 = test_rmse
3211/3211 [==============================] - 2s 664us/step
402/402 [==============================] - 0s 673us/step
402/402 [==============================] - 0s 682us/step
Evaluación Entrenamiento MAE: 13.840426445007324
Evaluación Validación MAE: 9.779932975769043
Evaluación Prueba MAE: 14.4535493850708
Entrenamiento RMSE: 15.8035306930542
Validación RMSE: 10.193990707397461
Prueba RMSE: 14.531745910644531

6. Gráfica¶

In [418]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [419]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 9ms/step
1/1 [==============================] - 0s 10ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-12 08:45:00           NaN       178.983139
2022-03-12 09:00:00           NaN       208.545502

Distintas arquitecturas CRNN probadas¶

1º Optimización de hiperparámetros¶

In [186]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_2_CRNN_24 = train_eval
val_eval_measurement_hypo_hype_2_CRNN_24 = val_eval
test_eval_measurement_hypo_hype_2_CRNN_24 = test_eval

train_rmse_measurement_hypo_hype_2_CRNN_24 = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN_24 = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN_24 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1606/1606 [==============================] - 3s 2ms/step - loss: 133.5308 - val_loss: 120.4234
Epoch 2/20
1606/1606 [==============================] - 3s 2ms/step - loss: 98.8586 - val_loss: 82.7842
Epoch 3/20
1606/1606 [==============================] - 3s 2ms/step - loss: 62.5007 - val_loss: 47.4328
Epoch 4/20
1606/1606 [==============================] - 3s 2ms/step - loss: 35.7188 - val_loss: 21.0515
Epoch 5/20
1606/1606 [==============================] - 2s 2ms/step - loss: 28.3419 - val_loss: 16.9870
Epoch 6/20
1606/1606 [==============================] - 2s 1ms/step - loss: 25.8684 - val_loss: 16.2216
Epoch 7/20
1606/1606 [==============================] - 2s 1ms/step - loss: 32.6291 - val_loss: 24.4362
Epoch 8/20
1606/1606 [==============================] - 2s 1ms/step - loss: 32.0655 - val_loss: 20.8216
Epoch 9/20
1606/1606 [==============================] - 2s 1ms/step - loss: 26.2422 - val_loss: 16.9353
3211/3211 [==============================] - 2s 539us/step
402/402 [==============================] - 0s 554us/step
402/402 [==============================] - 0s 564us/step
Evaluación Entrenamiento MAE: 22.284160614013672
Evaluación Validación MAE: 16.93526840209961
Evaluación Prueba MAE: 22.816110610961914
Entrenamiento RMSE: 30.645200729370117
Validación RMSE: 21.33058738708496
Prueba RMSE: 24.184709548950195

2º Optimización de hiperparámetros¶

In [187]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_1_CRNN_24 = train_eval
val_eval_measurement_hypo_hype_1_CRNN_24 = val_eval
test_eval_measurement_hypo_hype_1_CRNN_24 = test_eval

train_rmse_measurement_hypo_hype_1_CRNN_24 = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN_24 = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN_24 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1606/1606 [==============================] - 3s 2ms/step - loss: 124.2368 - val_loss: 102.9912
Epoch 2/20
1606/1606 [==============================] - 2s 1ms/step - loss: 77.6768 - val_loss: 57.9350
Epoch 3/20
1606/1606 [==============================] - 2s 1ms/step - loss: 44.4777 - val_loss: 30.9328
Epoch 4/20
1606/1606 [==============================] - 2s 1ms/step - loss: 31.2914 - val_loss: 20.9542
Epoch 5/20
1606/1606 [==============================] - 2s 1ms/step - loss: 26.7813 - val_loss: 16.6654
Epoch 6/20
1606/1606 [==============================] - 2s 1ms/step - loss: 31.2644 - val_loss: 18.1241
Epoch 7/20
1606/1606 [==============================] - 2s 1ms/step - loss: 26.9874 - val_loss: 16.9876
Epoch 8/20
1606/1606 [==============================] - 2s 1ms/step - loss: 27.8359 - val_loss: 12.7023
Epoch 9/20
1606/1606 [==============================] - 2s 1ms/step - loss: 26.6594 - val_loss: 14.6765
Epoch 10/20
1606/1606 [==============================] - 2s 1ms/step - loss: 23.4942 - val_loss: 13.5289
Epoch 11/20
1606/1606 [==============================] - 2s 1ms/step - loss: 22.0483 - val_loss: 15.4298
3211/3211 [==============================] - 2s 516us/step
402/402 [==============================] - 0s 534us/step
402/402 [==============================] - 0s 532us/step
Evaluación Entrenamiento MAE: 21.60335922241211
Evaluación Validación MAE: 15.429783821105957
Evaluación Prueba MAE: 21.31039810180664
Entrenamiento RMSE: 29.793907165527344
Validación RMSE: 20.155935287475586
Prueba RMSE: 22.769638061523438

Resultado obtenido con 3 arquitecturas distintas de la CRNN¶

Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement' + Hypoglycemia + Hyperglycemia

El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.

Las arquitecturas CRNN utilizadas son:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 64. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [420]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_1_CRNN vacía
tabla_1_CRNN_24 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_1_CRNN_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_1_CRNN_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_1_CRNN_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN_24
tabla_1_CRNN_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN_24
tabla_1_CRNN_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN_24

tabla_1_CRNN_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN_24
tabla_1_CRNN_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN_24
tabla_1_CRNN_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN_24

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(2) {
        text-align: center;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 21.603359
Validación MAE 15.429784
Test MAE 21.310398
Entrenamiento RMSE 29.793907
Validación RMSE 20.155935
Test RMSE 22.769638
In [421]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_1_CRNN_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_1_CRNN_24 = pd.concat([tabla_1_CRNN_24, pd.DataFrame([row])])

Tabla Arquitectura 2, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 64. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [422]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_2_CRNN vacía
tabla_2_CRNN_24 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_2_CRNN_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_2_CRNN_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_2_CRNN_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN_24
tabla_2_CRNN_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN_24
tabla_2_CRNN_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN_24

tabla_2_CRNN_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN_24
tabla_2_CRNN_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN_24
tabla_2_CRNN_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN_24

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 2, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 22.284161
Validación MAE 16.935268
Test MAE 22.816111
Entrenamiento RMSE 30.645201
Validación RMSE 21.330587
Test RMSE 24.184710
In [423]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_2_CRNN_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_2_CRNN_24 = pd.concat([tabla_2_CRNN_24, pd.DataFrame([row])])

Tabla Arquitectura 3, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [424]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_24 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_CRNN_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_CRNN_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_CRNN_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_24
tabla_3_CRNN_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_24
tabla_3_CRNN_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_24

tabla_3_CRNN_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_24
tabla_3_CRNN_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_24
tabla_3_CRNN_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_24

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 13.840426
Validación MAE 9.779933
Test MAE 14.453549
Entrenamiento RMSE 15.803531
Validación RMSE 10.193991
Test RMSE 14.531746
In [425]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_3_CRNN_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_3_CRNN_24 = pd.concat([tabla_3_CRNN_24, pd.DataFrame([row])])

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN¶

In [426]:
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN_24, tabla_2_CRNN_24, tabla_3_CRNN_24], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 21.603359 Measurement + Hypo_Hyper Entrenamiento MAE 22.284161 Measurement + Hypo_Hyper Entrenamiento MAE 13.840426 Measurement + Hypo_Hyper
Validación MAE 15.429784 Measurement + Hypo_Hyper Validación MAE 16.935268 Measurement + Hypo_Hyper Validación MAE 9.779933 Measurement + Hypo_Hyper
Test MAE 21.310398 Measurement + Hypo_Hyper Test MAE 22.816111 Measurement + Hypo_Hyper Test MAE 14.453549 Measurement + Hypo_Hyper
Entrenamiento RMSE 29.793907 Measurement + Hypo_Hyper Entrenamiento RMSE 30.645201 Measurement + Hypo_Hyper Entrenamiento RMSE 15.803531 Measurement + Hypo_Hyper
Validación RMSE 20.155935 Measurement + Hypo_Hyper Validación RMSE 21.330587 Measurement + Hypo_Hyper Validación RMSE 10.193991 Measurement + Hypo_Hyper
Test RMSE 22.769638 Measurement + Hypo_Hyper Test RMSE 24.184710 Measurement + Hypo_Hyper Test RMSE 14.531746 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, CRNN¶

In [427]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = tabla_1_CRNN_24.loc[tabla_1_CRNN_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_1_CRNN_24.loc[tabla_1_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = tabla_1_CRNN_24.loc[tabla_1_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = tabla_2_CRNN_24.loc[tabla_2_CRNN_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_2_CRNN_24.loc[tabla_2_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = tabla_2_CRNN_24.loc[tabla_2_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = tabla_3_CRNN_24.loc[tabla_3_CRNN_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_3_CRNN_24.loc[tabla_3_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = tabla_3_CRNN_24.loc[tabla_3_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
    {'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
CRNN_24 = styled_result.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_24}</div>'))

RESULTADO A MEJOR ARQUITECTURA, CRNN

ARQUITECTURA 3
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 13.840426 Measurement + Hypo_Hyper 3
Validación MAE 9.779933 Measurement + Hypo_Hyper 3
Test MAE 14.453549 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 15.803531 Measurement + Hypo_Hyper 3
Validación RMSE 10.193991 Measurement + Hypo_Hyper 3
Test RMSE 14.531746 Measurement + Hypo_Hyper 3

Conclusión CRNN¶

La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.

Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.

La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.

En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 3:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos.
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Validación RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjunto de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Entrenamiento MAE, Validación MAE, Test MAE, Entrenamiento RMSE, Validación RMSE y Test RMSE, estas evaluaciones determinan que es capaz de realizar un alto entrenamiento, alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.

Comparativa de resultados LSTM y CRNN¶

In [428]:
from IPython.display import display, HTML

html_tables_24 = f"""
<style>
    .table-wrapper {{
        display: inline-block;
        font-size: 14px;
        vertical-align: top;
        margin-right: 20px;
    }}
    .table-wrapper table {{
        font-size: 14px;
    }}
    .table-wrapper th, .table-wrapper td {{
        width: 100px;
    }}
    .table-wrapper h3 {{
        text-align: center;
        font-size: 18px;
        color: blue;
    }}
    h2 {{
        text-align: center;
    }}
</style>
<h2>Paciente 24</h2>
<div class="table-wrapper">
    <h3>LSTM</h3>
    {LSTM_24}
</div>
<div class="table-wrapper">
    <h3>CRNN</h3>
    {CRNN_24}
</div>
"""

display(HTML(html_tables_24))

Paciente 24

LSTM

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 1.848579 Measurement + Hypo_Hyper 2
Validación MAE 0.794391 Measurement + Hypo_Hyper 2
Test MAE 0.856952 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 5.513992 Measurement + Hypo_Hyper 2
Validación RMSE 0.927484 Measurement + Hypo_Hyper 2
Test RMSE 0.975038 Measurement + Hypo_Hyper 2

CRNN

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 13.840426 Measurement + Hypo_Hyper 3
Validación MAE 9.779933 Measurement + Hypo_Hyper 3
Test MAE 14.453549 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 15.803531 Measurement + Hypo_Hyper 3
Validación RMSE 10.193991 Measurement + Hypo_Hyper 3
Test RMSE 14.531746 Measurement + Hypo_Hyper 3

Mejor Modelo (Paciente 24)¶

In [429]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN_24': 1, 'tabla_2_CRNN_24': 2, 'tabla_3_CRNN_24': 3}
tables_arch2 = {'tabla_1_LSTM_24': 1, 'tabla_2_LSTM_24': 2, 'tabla_3_LSTM_24': 3}

# Para cada fila
for row in rows:
    min_vals = []
    
    # Arquitectura 1
    for table_name, arch in tables_arch1.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'CRNN', arch))

    # Arquitectura 2
    for table_name, arch in tables_arch2.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'LSTM', arch))

    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])

    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')

    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [min_arch],
        'Arquitectura (Hiperparámetros)': [min_num]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 24)</span></center>"
display(HTML(html_text))

# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()

# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))

# Nombrando
styled_result2 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result2 = styled_result2.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_24.style.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"

# Muestra el HTML
display(HTML(html))

# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))


# Mostrar la figura
display(fig_hypo_hype_2_LSTM_24)

# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_24.style.set_properties(**{'text-align': 'center'}).hide(axis='index')

# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"

# Mostrar la tabla centrada
display(HTML(table_html))

MEJOR ARQUITECTURA (Paciente 24)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 1.848579 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.794391 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.856952 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 5.513992 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 0.927484 Measurement + Hypo_Hyper LSTM 2
Test RMSE 0.975038 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-03-12 08:45:00 nan 156.134811
2022-03-12 09:00:00 nan 155.769943

Precisión del modelo con Clarke Error Grid

Zona Conteo Proporción
A 12836 100.00%
B 0 0.00%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Conclusión Paciente 24¶

Los resultados nos aclara que la mejor opción para el paciente 24 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.

Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

La conclusión final para el paciente 24 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hypo_Hyper para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.

Paciente 11
¶

La comparativa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 11 debido a que tras un análisis extenso se ha comprobado que es el 2º que tiene más mediciones.

Se va a llevar a cabo la comparativa de soluciones con los modelos RNN y CRNN pero con la mejor solución ya obtenida con el paciente 22. De esta manera se va a trabajar con la mejor arquitectura de hiperparámetros y de características de entrada así se va a poder confirmar que el experimento se ha realizado con éxito.

Las 3 arquitecturas de hiperparámetros de LSTM y CRNN son:

Arquitectura de hiperparámetros de LSTM:

  • Arquitectura 1(hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 64. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • return_sequences: True.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5.
    • units: 64. Número de neuronas en la capa LSTM.
    • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Arquitectura de hiperparámetros de CRNN:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

La característica de entrada va a ser: 'Measurement + Hypo_Hyper'

In [198]:
# Filtrar los datos para el paciente 11
df_paciente_11 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 11]

# Imprimir los resultados
df_paciente_11
Out[198]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-06-12 20:15:00 11 2018-06-12 1900-01-01 20:18:00 178 True False False 20 1
2018-06-12 20:30:00 11 2018-06-12 1900-01-01 20:33:00 178 True False False 20 1
2018-06-12 20:45:00 11 2018-06-12 1900-01-01 20:48:00 166 True False False 20 1
2018-06-12 21:00:00 11 2018-06-12 1900-01-01 21:03:00 157 True False False 21 1
2018-06-12 21:15:00 11 2018-06-12 1900-01-01 21:17:00 131 True False False 21 1
... ... ... ... ... ... ... ... ... ...
2022-02-25 10:30:00 11 2022-02-25 1900-01-01 10:30:00 174 True False False 10 4
2022-02-25 11:00:00 11 2022-02-25 1900-01-01 11:00:00 171 True False False 11 4
2022-02-25 11:15:00 11 2022-02-25 1900-01-01 11:15:00 167 True False False 11 4
2022-02-25 11:30:00 11 2022-02-25 1900-01-01 11:30:00 164 True False False 11 4
2022-02-25 19:15:00 11 2022-02-25 1900-01-01 19:15:00 178 True False False 19 4

11332 rows × 9 columns

Red Neuronal Recurrente (RNN) con el algoritmo Long Short-Term Memory (LSTM)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo LSTM con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'¶

En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal
  • "Hyperglycemia" indica si una medición está por encima del rango normal.

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [199]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [200]:
df
Out[200]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-06-12 20:18:00 178 False False
2018-06-12 20:33:00 178 False False
2018-06-12 20:48:00 166 False False
2018-06-12 21:03:00 157 False False
2018-06-12 21:17:00 131 False False
... ... ... ...
2022-02-25 10:30:00 174 False False
2022-02-25 11:00:00 171 False False
2022-02-25 11:15:00 167 False False
2022-02-25 11:30:00 164 False False
2022-02-25 19:15:00 178 False False

11332 rows × 3 columns

2. Dividir el conjunto de datos¶

In [201]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [202]:
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_27"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_34 (LSTM)              (None, 64)                17408     
                                                                 
 dense_27 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [203]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1625/1625 [==============================] - 6s 3ms/step - loss: 3.7017 - val_loss: 1.8222
Epoch 2/20
1625/1625 [==============================] - 5s 3ms/step - loss: 2.4894 - val_loss: 1.0617
Epoch 3/20
1625/1625 [==============================] - 5s 3ms/step - loss: 2.7856 - val_loss: 1.9999
Epoch 4/20
1625/1625 [==============================] - 5s 3ms/step - loss: 2.4887 - val_loss: 0.6329
Epoch 5/20
1625/1625 [==============================] - 5s 3ms/step - loss: 2.3033 - val_loss: 2.0450
Epoch 6/20
1625/1625 [==============================] - 5s 3ms/step - loss: 2.0181 - val_loss: 0.3167
Epoch 7/20
1625/1625 [==============================] - 5s 3ms/step - loss: 1.8943 - val_loss: 1.3279
Epoch 8/20
1625/1625 [==============================] - 5s 3ms/step - loss: 1.7665 - val_loss: 1.5087
Epoch 9/20
1625/1625 [==============================] - 5s 3ms/step - loss: 1.7000 - val_loss: 0.2722
Epoch 10/20
1625/1625 [==============================] - 5s 3ms/step - loss: 1.7749 - val_loss: 0.3763
Epoch 11/20
1625/1625 [==============================] - 5s 3ms/step - loss: 1.5242 - val_loss: 0.3488
Epoch 12/20
1625/1625 [==============================] - 5s 3ms/step - loss: 1.5787 - val_loss: 0.8497

5. Evaluar¶

In [204]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2_LSTM_11 = train_eval
val_eval_measurement_hypo_hype_2_LSTM_11 = val_eval
test_eval_measurement_hypo_hype_2_LSTM_11 = test_eval

train_rmse_measurement_hypo_hype_2_LSTM_11 = train_rmse
val_rmse_measurement_hypo_hype_2_LSTM_11 = val_rmse
test_rmse_measurement_hypo_hype_2_LSTM_11 = test_rmse
3250/3250 [==============================] - 3s 829us/step
406/406 [==============================] - 0s 845us/step
406/406 [==============================] - 0s 862us/step
Evaluación Entrenamiento MAE: 1.4609659910202026
Evaluación Validación MAE: 0.8496881127357483
Evaluación Prueba MAE: 0.8687956929206848
Entrenamiento RMSE: 4.643505096435547
Validación RMSE: 1.4289164543151855
Prueba RMSE: 1.4068015813827515

6. Gráfica¶

In [205]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [206]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')

future_predictions_hypo_hype_2_LSTM_11 = future_predictions
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 9ms/step

                     Datos reales  Datos predichos
date                                              
2022-02-25 19:30:00           NaN       179.642593
2022-02-25 19:45:00           NaN       173.928604

6.2 Clarke Error Grid¶

In [207]:
def clarke_error_grid(ref_values, pred_values):
    assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))

    if max(ref_values) > 400 or max(pred_values) > 400:
        print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
    if min(ref_values) < 0 or min(pred_values) < 0:
        print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values),  min(pred_values)))

    plt.clf()
    plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
    plt.title("Clarke Error Grid")
    plt.xlabel("Reference Concentration (mg/dl)")
    plt.ylabel("Prediction Concentration (mg/dl)")
    plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.gca().set_facecolor('white')
    plt.gca().set_xlim([0, 400])
    plt.gca().set_ylim([0, 400])
    plt.gca().set_aspect((400)/(400))
    plt.plot([0, 400], [0, 400], ':', c='black')
    plt.plot([0, 175/3], [70, 70], '-', c='black')
    plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
    plt.plot([70, 70], [84, 400],'-', c='black')
    plt.plot([0, 70], [180, 180], '-', c='black')
    plt.plot([70, 290], [180, 400],'-', c='black')
    plt.plot([70, 70], [0, 56], '-', c='black')
    plt.plot([70, 400], [56, 320],'-', c='black')
    plt.plot([180, 180], [0, 70], '-', c='black')
    plt.plot([180, 400], [70, 70], '-', c='black')
    plt.plot([240, 240], [70, 180],'-', c='black')
    plt.plot([240, 400], [180, 180], '-', c='black')
    plt.plot([130, 180], [0, 70], '-', c='black')
    plt.text(30, 15, "A", fontsize=15)
    plt.text(370, 260, "B", fontsize=15)
    plt.text(280, 370, "B", fontsize=15)
    plt.text(160, 370, "C", fontsize=15)
    plt.text(160, 15, "C", fontsize=15)
    plt.text(30, 140, "D", fontsize=15)
    plt.text(370, 120, "D", fontsize=15)
    plt.text(30, 370, "E", fontsize=15)
    plt.text(370, 15, "E", fontsize=15)

    zone = [0] * 5
    for i in range(len(ref_values)):
        if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
            zone[0] += 1  # Zone A
        elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
            zone[4] += 1  # Zone E
        elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
            zone[2] += 1  # Zone C
        elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
            zone[3] += 1  # Zone D
        else:
            zone[1] += 1  # Zone B

    return plt, zone

ref_values = y_test
pred_values = y_pred_test

plt, zone = clarke_error_grid(ref_values, pred_values)

# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_11 = plt.gcf()

# Mostrar la figura
plt.show()

# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})

# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total

# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))

# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_11 = zone_df

# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_11.style.hide(axis="index"))
Conteo de Zonas:
Zona Conteo Proporción
A 12989 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [430]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')


# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1_LSTM_11 = train_eval
val_eval_measurement_hypo_hype_1_LSTM_11 = val_eval
test_eval_measurement_hypo_hype_1_LSTM_11 = test_eval

train_rmse_measurement_hypo_hype_1_LSTM_11 = train_rmse
val_rmse_measurement_hypo_hype_1_LSTM_11 = val_rmse
test_rmse_measurement_hypo_hype_1_LSTM_11 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_54"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_64 (LSTM)              (None, 32)                4608      
                                                                 
 dense_54 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1625/1625 [==============================] - 4s 2ms/step - loss: 122.8506 - val_loss: 118.6820
Epoch 2/20
1625/1625 [==============================] - 3s 2ms/step - loss: 86.1190 - val_loss: 82.7876
Epoch 3/20
1625/1625 [==============================] - 3s 2ms/step - loss: 52.5447 - val_loss: 49.1386
Epoch 4/20
1625/1625 [==============================] - 3s 2ms/step - loss: 36.4419 - val_loss: 24.5215
Epoch 5/20
1625/1625 [==============================] - 3s 2ms/step - loss: 25.8021 - val_loss: 16.5972
Epoch 6/20
1625/1625 [==============================] - 3s 2ms/step - loss: 13.1940 - val_loss: 10.0398
Epoch 7/20
1625/1625 [==============================] - 3s 2ms/step - loss: 7.8960 - val_loss: 6.0100
Epoch 8/20
1625/1625 [==============================] - 3s 2ms/step - loss: 5.3219 - val_loss: 2.3499
Epoch 9/20
1625/1625 [==============================] - 3s 2ms/step - loss: 3.9468 - val_loss: 1.6772
Epoch 10/20
1625/1625 [==============================] - 3s 2ms/step - loss: 3.3093 - val_loss: 0.9987
Epoch 11/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.7988 - val_loss: 1.5336
Epoch 12/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.5711 - val_loss: 0.5686
Epoch 13/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.3924 - val_loss: 0.4269
Epoch 14/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.3650 - val_loss: 0.4643
Epoch 15/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.2302 - val_loss: 0.3410
Epoch 16/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.2107 - val_loss: 1.1281
Epoch 17/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.1175 - val_loss: 0.6529
Epoch 18/20
1625/1625 [==============================] - 3s 2ms/step - loss: 1.9106 - val_loss: 1.3566
3250/3250 [==============================] - 2s 655us/step
406/406 [==============================] - 0s 679us/step
406/406 [==============================] - 0s 644us/step
Evaluación Entrenamiento MAE: 2.225130796432495
Evaluación Validación MAE: 1.356641411781311
Evaluación Prueba MAE: 1.5138938426971436
Entrenamiento RMSE: 5.859622478485107
Validación RMSE: 1.78261137008667
Prueba RMSE: 1.869420051574707

2º Optimización de hiperparámetros¶

In [209]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_LSTM_11 = train_eval
val_eval_measurement_hypo_hype_3_LSTM_11 = val_eval
test_eval_measurement_hypo_hype_3_LSTM_11 = test_eval

train_rmse_measurement_hypo_hype_3_LSTM_11 = train_rmse
val_rmse_measurement_hypo_hype_3_LSTM_11 = val_rmse
test_rmse_measurement_hypo_hype_3_LSTM_11 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_29"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_36 (LSTM)              (None, 8, 32)             4608      
                                                                 
 batch_normalization_18 (Bat  (None, 8, 32)            128       
 chNormalization)                                                
                                                                 
 dropout_18 (Dropout)        (None, 8, 32)             0         
                                                                 
 lstm_37 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_19 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_19 (Dropout)        (None, 64)                0         
                                                                 
 dense_29 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1625/1625 [==============================] - 9s 5ms/step - loss: 40.8846 - val_loss: 11.6269
Epoch 2/20
1625/1625 [==============================] - 8s 5ms/step - loss: 26.0059 - val_loss: 13.8111
Epoch 3/20
1625/1625 [==============================] - 8s 5ms/step - loss: 25.5797 - val_loss: 11.0185
Epoch 4/20
1625/1625 [==============================] - 8s 5ms/step - loss: 25.3500 - val_loss: 16.4315
Epoch 5/20
1625/1625 [==============================] - 8s 5ms/step - loss: 25.1992 - val_loss: 12.4309
Epoch 6/20
1625/1625 [==============================] - 8s 5ms/step - loss: 25.0538 - val_loss: 10.6724
Epoch 7/20
1625/1625 [==============================] - 8s 5ms/step - loss: 24.8869 - val_loss: 12.2761
Epoch 8/20
1625/1625 [==============================] - 8s 5ms/step - loss: 24.8269 - val_loss: 14.5116
Epoch 9/20
1625/1625 [==============================] - 8s 5ms/step - loss: 24.7012 - val_loss: 12.3753
3250/3250 [==============================] - 4s 1ms/step
406/406 [==============================] - 1s 1ms/step
406/406 [==============================] - 1s 1ms/step
Evaluación Entrenamiento MAE: 14.121617317199707
Evaluación Validación MAE: 12.37533950805664
Evaluación Prueba MAE: 13.596681594848633
Entrenamiento RMSE: 17.937429428100586
Validación RMSE: 12.754348754882812
Prueba RMSE: 13.90673542022705

Resultado obtenido con 3 arquitecturas distintas de la LSTM¶

Después de realizar las pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement' + Hypoglycemia + Hyperglycemia

El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.

Las arquitecturas LSTM utilizadas son:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 64. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • return_sequences: True.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5.
    • units: 64. Número de neuronas en la capa LSTM.
    • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [431]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_1_LSTM_11 vacía
tabla_1_LSTM_11 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_1_LSTM_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_LSTM_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_LSTM_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_1_LSTM_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_LSTM_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_LSTM_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_1_LSTM_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_LSTM_11
tabla_1_LSTM_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_LSTM_11
tabla_1_LSTM_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_LSTM_11

tabla_1_LSTM_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_LSTM_11
tabla_1_LSTM_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_LSTM_11
tabla_1_LSTM_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_LSTM_11

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(2) {
        text-align: center;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_LSTM_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 2.225131
Validación MAE 1.356641
Test MAE 1.513894
Entrenamiento RMSE 5.859622
Validación RMSE 1.782611
Test RMSE 1.869420
In [432]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_LSTM_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_1_LSTM_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_1_LSTM_11 = pd.concat([tabla_1_LSTM_11, pd.DataFrame([row])])

Tabla Arquitectura 2, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [433]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla vacía
tabla_2_LSTM_11 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_2_LSTM_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_LSTM_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_LSTM_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_2_LSTM_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_LSTM_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_LSTM_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_2_LSTM_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_LSTM_11
tabla_2_LSTM_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_LSTM_11
tabla_2_LSTM_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_LSTM_11

tabla_2_LSTM_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_LSTM_11
tabla_2_LSTM_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_LSTM_11
tabla_2_LSTM_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_LSTM_11

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_LSTM_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 2, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 1.460966
Validación MAE 0.849688
Test MAE 0.868796
Entrenamiento RMSE 4.643505
Validación RMSE 1.428916
Test RMSE 1.406802
In [434]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_LSTM_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_2_LSTM_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_2_LSTM_11 = pd.concat([tabla_2_LSTM_11, pd.DataFrame([row])])

Tabla Arquitectura 3, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • return_sequences: True.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5.
  • units: 64. Número de neuronas en la capa LSTM.
  • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [435]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_LSTM_11 vacía
tabla_3_LSTM_11 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_LSTM_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_LSTM_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_LSTM_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_LSTM_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_LSTM_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_LSTM_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_LSTM_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_LSTM_11
tabla_3_LSTM_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_LSTM_11
tabla_3_LSTM_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_LSTM_11

tabla_3_LSTM_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_LSTM_11
tabla_3_LSTM_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_LSTM_11
tabla_3_LSTM_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_LSTM_11

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_LSTM_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 14.121617
Validación MAE 12.375340
Test MAE 13.596682
Entrenamiento RMSE 17.937429
Validación RMSE 12.754349
Test RMSE 13.906735
In [436]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_LSTM_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_3_LSTM_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_3_LSTM_11 = pd.concat([tabla_3_LSTM_11, pd.DataFrame([row])])

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM¶

In [437]:
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_LSTM_11, tabla_2_LSTM_11, tabla_3_LSTM_11], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 2.225131 Measurement + Hypo_Hyper Entrenamiento MAE 1.460966 Measurement + Hypo_Hyper Entrenamiento MAE 14.121617 Measurement + Hypo_Hyper
Validación MAE 1.356641 Measurement + Hypo_Hyper Validación MAE 0.849688 Measurement + Hypo_Hyper Validación MAE 12.375340 Measurement + Hypo_Hyper
Test MAE 1.513894 Measurement + Hypo_Hyper Test MAE 0.868796 Measurement + Hypo_Hyper Test MAE 13.596682 Measurement + Hypo_Hyper
Entrenamiento RMSE 5.859622 Measurement + Hypo_Hyper Entrenamiento RMSE 4.643505 Measurement + Hypo_Hyper Entrenamiento RMSE 17.937429 Measurement + Hypo_Hyper
Validación RMSE 1.782611 Measurement + Hypo_Hyper Validación RMSE 1.428916 Measurement + Hypo_Hyper Validación RMSE 12.754349 Measurement + Hypo_Hyper
Test RMSE 1.869420 Measurement + Hypo_Hyper Test RMSE 1.406802 Measurement + Hypo_Hyper Test RMSE 13.906735 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, LSTM¶

In [438]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = tabla_1_LSTM_11.loc[tabla_1_LSTM_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_1_LSTM_11.loc[tabla_1_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = tabla_1_LSTM_11.loc[tabla_1_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = tabla_2_LSTM_11.loc[tabla_2_LSTM_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_2_LSTM_11.loc[tabla_2_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = tabla_2_LSTM_11.loc[tabla_2_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = tabla_3_LSTM_11.loc[tabla_3_LSTM_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_3_LSTM_11.loc[tabla_3_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = tabla_3_LSTM_11.loc[tabla_3_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
    {'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_11 = styled_result.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_11}</div>'))

RESULTADO A MEJOR ARQUITECTURA, LSTM

ARQUITECTURA 2
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 1.460966 Measurement + Hypo_Hyper 2
Validación MAE 0.849688 Measurement + Hypo_Hyper 2
Test MAE 0.868796 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 4.643505 Measurement + Hypo_Hyper 2
Validación RMSE 1.428916 Measurement + Hypo_Hyper 2
Test RMSE 1.406802 Measurement + Hypo_Hyper 2

Conclusión LSTM¶

La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.

La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:

  1. Puerta de entrada (input gate): Determina qué nueva información debe agregarse al estado actual.
  2. Puerta de olvido (forget gate): Determina qué información antigua debe descartarse del estado actual.
  3. Puerta de salida (output gate): Determina qué parte del estado actual debe emitirse como salida. Además, las puertas contienen funciones de activación.

El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.

En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 2:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos.
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Validación RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjunto de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Entrenamiento MAE, Validación MAE, Test MAE, Entrenamiento RMSE, Validación RMSE y Test RMSE, estas evaluaciones determinan que es capaz de realizar un alto entrenamiento, alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con LSTM con las características de entrada Measurement + Hypo_Hyper.

Redes Neuronales Recurrentes Convolucionales (CRNN)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo CRNN con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.¶

En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hyperglycemia" indica si una medición está por encima del rango normal.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal

Mejor arquitectura 3¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [218]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [219]:
df
Out[219]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-06-12 20:18:00 178 False False
2018-06-12 20:33:00 178 False False
2018-06-12 20:48:00 166 False False
2018-06-12 21:03:00 157 False False
2018-06-12 21:17:00 131 False False
... ... ... ...
2022-02-25 10:30:00 174 False False
2022-02-25 11:00:00 171 False False
2022-02-25 11:15:00 167 False False
2022-02-25 11:30:00 164 False False
2022-02-25 19:15:00 178 False False

11332 rows × 3 columns

2. Dividir el conjunto de datos¶

In [220]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [221]:
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_30"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv1d_6 (Conv1D)           (None, 6, 32)             320       
                                                                 
 batch_normalization_20 (Bat  (None, 6, 32)            128       
 chNormalization)                                                
                                                                 
 max_pooling1d_6 (MaxPooling  (None, 3, 32)            0         
 1D)                                                             
                                                                 
 dropout_20 (Dropout)        (None, 3, 32)             0         
                                                                 
 lstm_38 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_21 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_21 (Dropout)        (None, 64)                0         
                                                                 
 dense_30 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________

4. Entrenar el modelo¶

In [222]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1625/1625 [==============================] - 5s 2ms/step - loss: 41.7783 - val_loss: 18.1101
Epoch 2/20
1625/1625 [==============================] - 4s 2ms/step - loss: 24.9494 - val_loss: 12.0912
Epoch 3/20
1625/1625 [==============================] - 4s 2ms/step - loss: 24.6390 - val_loss: 19.6754
Epoch 4/20
1625/1625 [==============================] - 4s 2ms/step - loss: 24.6374 - val_loss: 22.1808
Epoch 5/20
1625/1625 [==============================] - 4s 2ms/step - loss: 24.6893 - val_loss: 3.5177
Epoch 6/20
1625/1625 [==============================] - 4s 2ms/step - loss: 24.6268 - val_loss: 7.0594
Epoch 7/20
1625/1625 [==============================] - 4s 2ms/step - loss: 24.6278 - val_loss: 11.3842
Epoch 8/20
1625/1625 [==============================] - 4s 2ms/step - loss: 24.5211 - val_loss: 7.6010

5. Evaluar¶

In [223]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN_11 = train_eval
val_eval_measurement_hypo_hype_3_CRNN_11 = val_eval
test_eval_measurement_hypo_hype_3_CRNN_11 = test_eval

train_rmse_measurement_hypo_hype_3_CRNN_11 = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN_11 = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN_11 = test_rmse
3250/3250 [==============================] - 2s 703us/step
406/406 [==============================] - 0s 688us/step
406/406 [==============================] - 0s 693us/step
Evaluación Entrenamiento MAE: 13.247295379638672
Evaluación Validación MAE: 7.600970268249512
Evaluación Prueba MAE: 7.071486473083496
Entrenamiento RMSE: 17.684404373168945
Validación RMSE: 8.325462341308594
Prueba RMSE: 7.5740556716918945

6. Gráfica¶

In [224]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [225]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 11ms/step

                     Datos reales  Datos predichos
date                                              
2022-02-25 19:30:00           NaN       168.789627
2022-02-25 19:45:00           NaN       168.962662

Distintas arquitecturas CRNN probadas¶

1º Optimización de hiperparámetros¶

In [226]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_2_CRNN_11 = train_eval
val_eval_measurement_hypo_hype_2_CRNN_11 = val_eval
test_eval_measurement_hypo_hype_2_CRNN_11 = test_eval

train_rmse_measurement_hypo_hype_2_CRNN_11 = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN_11 = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN_11 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1625/1625 [==============================] - 3s 2ms/step - loss: 119.8955 - val_loss: 112.7883
Epoch 2/20
1625/1625 [==============================] - 2s 1ms/step - loss: 75.4090 - val_loss: 67.8727
Epoch 3/20
1625/1625 [==============================] - 2s 1ms/step - loss: 51.0689 - val_loss: 45.4892
Epoch 4/20
1625/1625 [==============================] - 2s 1ms/step - loss: 50.7536 - val_loss: 48.2311
Epoch 5/20
1625/1625 [==============================] - 2s 1ms/step - loss: 57.1844 - val_loss: 59.5624
Epoch 6/20
1625/1625 [==============================] - 2s 1ms/step - loss: 58.2487 - val_loss: 61.5965
3250/3250 [==============================] - 2s 545us/step
406/406 [==============================] - 0s 544us/step
406/406 [==============================] - 0s 553us/step
Evaluación Entrenamiento MAE: 49.19638442993164
Evaluación Validación MAE: 61.59640884399414
Evaluación Prueba MAE: 65.90991973876953
Entrenamiento RMSE: 54.69769287109375
Validación RMSE: 62.3424186706543
Prueba RMSE: 66.49855041503906

2º Optimización de hiperparámetros¶

In [227]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_1_CRNN_11 = train_eval
val_eval_measurement_hypo_hype_1_CRNN_11 = val_eval
test_eval_measurement_hypo_hype_1_CRNN_11 = test_eval

train_rmse_measurement_hypo_hype_1_CRNN_11 = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN_11 = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN_11 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1625/1625 [==============================] - 3s 1ms/step - loss: 125.1699 - val_loss: 121.2117
Epoch 2/20
1625/1625 [==============================] - 2s 1ms/step - loss: 87.0778 - val_loss: 82.7230
Epoch 3/20
1625/1625 [==============================] - 2s 1ms/step - loss: 52.2566 - val_loss: 48.1539
Epoch 4/20
1625/1625 [==============================] - 2s 1ms/step - loss: 53.7223 - val_loss: 52.8987
Epoch 5/20
1625/1625 [==============================] - 2s 1ms/step - loss: 55.2015 - val_loss: 57.0709
Epoch 6/20
1625/1625 [==============================] - 2s 1ms/step - loss: 51.4649 - val_loss: 47.6335
Epoch 7/20
1625/1625 [==============================] - 2s 1ms/step - loss: 50.0839 - val_loss: 51.8648
Epoch 8/20
1625/1625 [==============================] - 2s 1ms/step - loss: 52.8192 - val_loss: 55.3951
Epoch 9/20
1625/1625 [==============================] - 2s 1ms/step - loss: 52.1573 - val_loss: 51.6825
3250/3250 [==============================] - 2s 507us/step
406/406 [==============================] - 0s 501us/step
406/406 [==============================] - 0s 514us/step
Evaluación Entrenamiento MAE: 40.94864273071289
Evaluación Validación MAE: 51.68236541748047
Evaluación Prueba MAE: 55.995872497558594
Entrenamiento RMSE: 46.460914611816406
Validación RMSE: 52.56913375854492
Prueba RMSE: 56.68741226196289

Resultado obtenido con 3 arquitecturas distintas de la CRNN¶

Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement' + Hypoglycemia + Hyperglycemia

El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.

Las arquitecturas CRNN utilizadas son:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 64. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [228]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_1_CRNN vacía
tabla_1_CRNN_11 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_1_CRNN_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_1_CRNN_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_1_CRNN_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN_11
tabla_1_CRNN_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN_11
tabla_1_CRNN_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN_11

tabla_1_CRNN_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN_11
tabla_1_CRNN_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN_11
tabla_1_CRNN_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN_11

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(2) {
        text-align: center;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 40.948643
Validación MAE 51.682365
Test MAE 55.995872
Entrenamiento RMSE 46.460915
Validación RMSE 52.569134
Test RMSE 56.687412
In [229]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_1_CRNN_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_1_CRNN_11 = pd.concat([tabla_1_CRNN_11, pd.DataFrame([row])])

Tabla Arquitectura 2, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 64. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [230]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_2_CRNN vacía
tabla_2_CRNN_11 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_2_CRNN_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_2_CRNN_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_2_CRNN_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN_11
tabla_2_CRNN_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN_11
tabla_2_CRNN_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN_11

tabla_2_CRNN_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN_11
tabla_2_CRNN_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN_11
tabla_2_CRNN_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN_11

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 2, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 49.196384
Validación MAE 61.596409
Test MAE 65.909920
Entrenamiento RMSE 54.697693
Validación RMSE 62.342419
Test RMSE 66.498550
In [231]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_2_CRNN_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_2_CRNN_11 = pd.concat([tabla_2_CRNN_11, pd.DataFrame([row])])

Tabla Arquitectura 3, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [232]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_11 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_CRNN_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_CRNN_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_CRNN_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_11
tabla_3_CRNN_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_11
tabla_3_CRNN_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_11

tabla_3_CRNN_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_11
tabla_3_CRNN_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_11
tabla_3_CRNN_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_11

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 13.247295
Validación MAE 7.600970
Test MAE 7.071486
Entrenamiento RMSE 17.684404
Validación RMSE 8.325462
Test RMSE 7.574056
In [233]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_3_CRNN_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_3_CRNN_11 = pd.concat([tabla_3_CRNN_11, pd.DataFrame([row])])

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN¶

In [234]:
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN_11, tabla_2_CRNN_11, tabla_3_CRNN_11], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 40.948643 Measurement + Hypo_Hyper Entrenamiento MAE 49.196384 Measurement + Hypo_Hyper Entrenamiento MAE 13.247295 Measurement + Hypo_Hyper
Validación MAE 51.682365 Measurement + Hypo_Hyper Validación MAE 61.596409 Measurement + Hypo_Hyper Validación MAE 7.600970 Measurement + Hypo_Hyper
Test MAE 55.995872 Measurement + Hypo_Hyper Test MAE 65.909920 Measurement + Hypo_Hyper Test MAE 7.071486 Measurement + Hypo_Hyper
Entrenamiento RMSE 46.460915 Measurement + Hypo_Hyper Entrenamiento RMSE 54.697693 Measurement + Hypo_Hyper Entrenamiento RMSE 17.684404 Measurement + Hypo_Hyper
Validación RMSE 52.569134 Measurement + Hypo_Hyper Validación RMSE 62.342419 Measurement + Hypo_Hyper Validación RMSE 8.325462 Measurement + Hypo_Hyper
Test RMSE 56.687412 Measurement + Hypo_Hyper Test RMSE 66.498550 Measurement + Hypo_Hyper Test RMSE 7.574056 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, CRNN¶

In [235]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = tabla_1_CRNN_11.loc[tabla_1_CRNN_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_1_CRNN_11.loc[tabla_1_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = tabla_1_CRNN_11.loc[tabla_1_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = tabla_2_CRNN_11.loc[tabla_2_CRNN_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_2_CRNN_11.loc[tabla_2_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = tabla_2_CRNN_11.loc[tabla_2_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = tabla_3_CRNN.loc[tabla_3_CRNN_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_3_CRNN.loc[tabla_3_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = tabla_3_CRNN.loc[tabla_3_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
    {'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
CRNN_11 = styled_result.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_11}</div>'))

RESULTADO A MEJOR ARQUITECTURA, CRNN

ARQUITECTURA 3
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 15.537673 Measurement + Hypo_Hyper 3
Validación MAE 15.351344 Measurement + Hypo_Hyper 3
Test MAE 14.596274 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 19.507032 Measurement + Hypo_Hyper 3
Validación RMSE 16.051613 Measurement + Hypo_Hyper 3
Test RMSE 16.385645 Measurement + Hypo_Hyper 3

Conclusión CRNN¶

La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.

Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.

La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.

En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 3 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 3:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos.
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Validación RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjunto de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Entrenamiento MAE, Validación MAE, Test MAE, Entrenamiento RMSE, Validación RMSE y Test RMSE, estas evaluaciones determinan que es capaz de realizar un alto entrenamiento, alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.

Comparativa de resultados LSTM y CRNN¶

In [439]:
from IPython.display import display, HTML

html_tables_11 = f"""
<style>
    .table-wrapper {{
        display: inline-block;
        font-size: 14px;
        vertical-align: top;
        margin-right: 20px;
    }}
    .table-wrapper table {{
        font-size: 14px;
    }}
    .table-wrapper th, .table-wrapper td {{
        width: 100px;
    }}
    .table-wrapper h3 {{
        text-align: center;
        font-size: 18px;
        color: blue;
    }}
    h2 {{
        text-align: center;
    }}
</style>
<h2>Paciente 11</h2>
<div class="table-wrapper">
    <h3>LSTM</h3>
    {LSTM_11}
</div>
<div class="table-wrapper">
    <h3>CRNN</h3>
    {CRNN_11}
</div>
"""

display(HTML(html_tables_11))

Paciente 11

LSTM

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 1.460966 Measurement + Hypo_Hyper 2
Validación MAE 0.849688 Measurement + Hypo_Hyper 2
Test MAE 0.868796 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 4.643505 Measurement + Hypo_Hyper 2
Validación RMSE 1.428916 Measurement + Hypo_Hyper 2
Test RMSE 1.406802 Measurement + Hypo_Hyper 2

CRNN

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 15.537673 Measurement + Hypo_Hyper 3
Validación MAE 15.351344 Measurement + Hypo_Hyper 3
Test MAE 14.596274 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 19.507032 Measurement + Hypo_Hyper 3
Validación RMSE 16.051613 Measurement + Hypo_Hyper 3
Test RMSE 16.385645 Measurement + Hypo_Hyper 3

Mejor Modelo (Paciente 11)¶

In [440]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN_11': 1, 'tabla_2_CRNN_11': 2, 'tabla_3_CRNN_11': 3}
tables_arch2 = {'tabla_1_LSTM_11': 1, 'tabla_2_LSTM_11': 2, 'tabla_3_LSTM_11': 3}

# Para cada fila
for row in rows:
    min_vals = []
    
    # Arquitectura 1
    for table_name, arch in tables_arch1.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'CRNN', arch))

    # Arquitectura 2
    for table_name, arch in tables_arch2.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'LSTM', arch))

    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])

    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')

    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [min_arch],
        'Arquitectura (Hiperparámetros)': [min_num]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 11)</span></center>"
display(HTML(html_text))

# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()

# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))

# Nombrando
styled_result3 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result3 = styled_result3.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_11.style.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"

# Muestra el HTML
display(HTML(html))

# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))

# Mostrar la figura
display(fig_hypo_hype_2_LSTM_11)

# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_11.style.set_properties(**{'text-align': 'center'}).hide(axis='index')

# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"

# Mostrar la tabla centrada
display(HTML(table_html))

MEJOR ARQUITECTURA (Paciente 11)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 1.460966 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.849688 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.868796 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 4.643505 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 1.428916 Measurement + Hypo_Hyper LSTM 2
Test RMSE 1.406802 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-02-25 19:30:00 nan 179.642593
2022-02-25 19:45:00 nan 173.928604

Precisión del modelo con Clarke Error Grid

Zona Conteo Proporción
A 12989 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Conclusión Paciente 11¶

Los resultados nos aclara que la mejor opción para el paciente 11 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.

Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

La conclusión final para el paciente 11 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hypo_Hyper para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.

Paciente 83
¶

La comparativa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 83 debido a que tras un análisis extenso se ha comprobado que es el 4º con mayor número de mediciones.

Se va a llevar a cabo la comparativa de soluciones con los modelos RNN y CRNN pero con la mejor solución ya obtenida con el paciente 22. De esta manera se va a trabajar con la mejor arquitectura de hiperparámetros y de características de entrada así se va a poder confirmar que el experimento se ha realizado con éxito.

Las 3 arquitecturas de hiperparámetros de LSTM y CRNN son:

Arquitectura de hiperparámetros de LSTM:

  • Arquitectura 1(hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 64. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • return_sequences: True.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5.
    • units: 64. Número de neuronas en la capa LSTM.
    • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Arquitectura de hiperparámetros de CRNN:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

La característica de entrada va a ser: 'Measurement + Hypo_Hyper'

In [238]:
# Filtrar los datos para el paciente 83
df_paciente_83 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 83]

# Imprimir los resultados
df_paciente_83
Out[238]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-02-21 19:45:00 83 2018-02-21 1900-01-01 19:46:00 121 True False False 19 2
2018-02-21 20:00:00 83 2018-02-21 1900-01-01 20:01:00 124 True False False 20 2
2018-02-21 20:15:00 83 2018-02-21 1900-01-01 20:17:00 131 True False False 20 2
2018-02-21 20:30:00 83 2018-02-21 1900-01-01 20:32:00 128 True False False 20 2
2018-02-21 20:45:00 83 2018-02-21 1900-01-01 20:47:00 129 True False False 20 2
... ... ... ... ... ... ... ... ... ...
2022-03-14 06:45:00 83 2022-03-14 1900-01-01 06:45:00 159 True False False 6 0
2022-03-14 07:00:00 83 2022-03-14 1900-01-01 07:00:00 160 True False False 7 0
2022-03-14 07:15:00 83 2022-03-14 1900-01-01 07:15:00 164 True False False 7 0
2022-03-14 10:00:00 83 2022-03-14 1900-01-01 10:00:00 182 True False False 10 0
2022-03-14 10:45:00 83 2022-03-14 1900-01-01 10:45:00 168 True False False 10 0

9198 rows × 9 columns

Red Neuronal Recurrente (RNN) con el algoritmo Long Short-Term Memory (LSTM)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo LSTM con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'¶

En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal
  • "Hyperglycemia" indica si una medición está por encima del rango normal.

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [239]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [240]:
df
Out[240]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-02-21 19:46:00 121 False False
2018-02-21 20:01:00 124 False False
2018-02-21 20:17:00 131 False False
2018-02-21 20:32:00 128 False False
2018-02-21 20:47:00 129 False False
... ... ... ...
2022-03-14 06:45:00 159 False False
2022-03-14 07:00:00 160 False False
2022-03-14 07:15:00 164 False False
2022-03-14 10:00:00 182 False False
2022-03-14 10:45:00 168 False False

9198 rows × 3 columns

2. Dividir el conjunto de datos¶

In [241]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [242]:
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_33"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_41 (LSTM)              (None, 64)                17408     
                                                                 
 dense_33 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [243]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1778/1778 [==============================] - 5s 2ms/step - loss: 2.9965 - val_loss: 1.8547
Epoch 2/20
1778/1778 [==============================] - 4s 2ms/step - loss: 2.0011 - val_loss: 0.8204
Epoch 3/20
1778/1778 [==============================] - 4s 2ms/step - loss: 2.0433 - val_loss: 1.8748
Epoch 4/20
1778/1778 [==============================] - 4s 2ms/step - loss: 1.7310 - val_loss: 0.6916
Epoch 5/20
1778/1778 [==============================] - 4s 2ms/step - loss: 1.7213 - val_loss: 2.5189
Epoch 6/20
1778/1778 [==============================] - 4s 2ms/step - loss: 1.5553 - val_loss: 0.6286
Epoch 7/20
1778/1778 [==============================] - 4s 2ms/step - loss: 1.3384 - val_loss: 0.5956
Epoch 8/20
1778/1778 [==============================] - 4s 2ms/step - loss: 1.5467 - val_loss: 0.8822
Epoch 9/20
1778/1778 [==============================] - 4s 2ms/step - loss: 1.3162 - val_loss: 1.3294
Epoch 10/20
1778/1778 [==============================] - 4s 2ms/step - loss: 1.2281 - val_loss: 0.6102

5. Evaluar¶

In [244]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2_LSTM_83 = train_eval
val_eval_measurement_hypo_hype_2_LSTM_83 = val_eval
test_eval_measurement_hypo_hype_2_LSTM_83 = test_eval

train_rmse_measurement_hypo_hype_2_LSTM_83 = train_rmse
val_rmse_measurement_hypo_hype_2_LSTM_83 = val_rmse
test_rmse_measurement_hypo_hype_2_LSTM_83 = test_rmse
3556/3556 [==============================] - 3s 787us/step
445/445 [==============================] - 0s 788us/step
445/445 [==============================] - 0s 791us/step
Evaluación Entrenamiento MAE: 0.8629071712493896
Evaluación Validación MAE: 0.6101782321929932
Evaluación Prueba MAE: 0.465580016374588
Entrenamiento RMSE: 3.4258315563201904
Validación RMSE: 1.7380841970443726
Prueba RMSE: 1.2392966747283936

6. Gráfica¶

In [245]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [246]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')

# Almacena dato
future_predictions_hypo_hype_2_LSTM_83 = future_predictions
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 9ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-14 11:00:00           NaN       166.793915
2022-03-14 11:15:00           NaN       165.903671

6.2 Clarke Error Grid¶

In [247]:
def clarke_error_grid(ref_values, pred_values):
    assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))

    if max(ref_values) > 400 or max(pred_values) > 400:
        print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
    if min(ref_values) < 0 or min(pred_values) < 0:
        print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values),  min(pred_values)))

    plt.clf()
    plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
    plt.title("Clarke Error Grid")
    plt.xlabel("Reference Concentration (mg/dl)")
    plt.ylabel("Prediction Concentration (mg/dl)")
    plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.gca().set_facecolor('white')
    plt.gca().set_xlim([0, 400])
    plt.gca().set_ylim([0, 400])
    plt.gca().set_aspect((400)/(400))
    plt.plot([0, 400], [0, 400], ':', c='black')
    plt.plot([0, 175/3], [70, 70], '-', c='black')
    plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
    plt.plot([70, 70], [84, 400],'-', c='black')
    plt.plot([0, 70], [180, 180], '-', c='black')
    plt.plot([70, 290], [180, 400],'-', c='black')
    plt.plot([70, 70], [0, 56], '-', c='black')
    plt.plot([70, 400], [56, 320],'-', c='black')
    plt.plot([180, 180], [0, 70], '-', c='black')
    plt.plot([180, 400], [70, 70], '-', c='black')
    plt.plot([240, 240], [70, 180],'-', c='black')
    plt.plot([240, 400], [180, 180], '-', c='black')
    plt.plot([130, 180], [0, 70], '-', c='black')
    plt.text(30, 15, "A", fontsize=15)
    plt.text(370, 260, "B", fontsize=15)
    plt.text(280, 370, "B", fontsize=15)
    plt.text(160, 370, "C", fontsize=15)
    plt.text(160, 15, "C", fontsize=15)
    plt.text(30, 140, "D", fontsize=15)
    plt.text(370, 120, "D", fontsize=15)
    plt.text(30, 370, "E", fontsize=15)
    plt.text(370, 15, "E", fontsize=15)

    zone = [0] * 5
    for i in range(len(ref_values)):
        if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
            zone[0] += 1  # Zone A
        elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
            zone[4] += 1  # Zone E
        elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
            zone[2] += 1  # Zone C
        elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
            zone[3] += 1  # Zone D
        else:
            zone[1] += 1  # Zone B

    return plt, zone

ref_values = y_test
pred_values = y_pred_test

plt, zone = clarke_error_grid(ref_values, pred_values)

# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_83 = plt.gcf()

# Mostrar la figura
plt.show()

# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})

# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total

# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))

# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_83 = zone_df

# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_83.style.hide(axis="index"))
Conteo de Zonas:
Zona Conteo Proporción
A 14216 100.00%
B 0 0.00%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [248]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')


# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1_LSTM_83 = train_eval
val_eval_measurement_hypo_hype_1_LSTM_83 = val_eval
test_eval_measurement_hypo_hype_1_LSTM_83 = test_eval

train_rmse_measurement_hypo_hype_1_LSTM_83 = train_rmse
val_rmse_measurement_hypo_hype_1_LSTM_83 = val_rmse
test_rmse_measurement_hypo_hype_1_LSTM_83 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_34"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_42 (LSTM)              (None, 32)                4608      
                                                                 
 dense_34 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1778/1778 [==============================] - 4s 2ms/step - loss: 123.7073 - val_loss: 113.1900
Epoch 2/20
1778/1778 [==============================] - 3s 2ms/step - loss: 83.3717 - val_loss: 73.8789
Epoch 3/20
1778/1778 [==============================] - 3s 2ms/step - loss: 45.5802 - val_loss: 33.9464
Epoch 4/20
1778/1778 [==============================] - 3s 2ms/step - loss: 44.3730 - val_loss: 34.0456
Epoch 5/20
1778/1778 [==============================] - 3s 2ms/step - loss: 40.5904 - val_loss: 21.5790
Epoch 6/20
1778/1778 [==============================] - 3s 2ms/step - loss: 34.1987 - val_loss: 21.2392
Epoch 7/20
1778/1778 [==============================] - 3s 2ms/step - loss: 28.9748 - val_loss: 15.3343
Epoch 8/20
1778/1778 [==============================] - 3s 2ms/step - loss: 38.4588 - val_loss: 26.2944
Epoch 9/20
1778/1778 [==============================] - 3s 2ms/step - loss: 35.9527 - val_loss: 27.6839
Epoch 10/20
1778/1778 [==============================] - 3s 2ms/step - loss: 37.1429 - val_loss: 27.5536
3556/3556 [==============================] - 2s 618us/step
445/445 [==============================] - 0s 624us/step
445/445 [==============================] - 0s 621us/step
Evaluación Entrenamiento MAE: 25.64620590209961
Evaluación Validación MAE: 27.55359649658203
Evaluación Prueba MAE: 36.11166000366211
Entrenamiento RMSE: 30.676008224487305
Validación RMSE: 29.090604782104492
Prueba RMSE: 37.675025939941406

2º Optimización de hiperparámetros¶

In [249]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_LSTM_83 = train_eval
val_eval_measurement_hypo_hype_3_LSTM_83 = val_eval
test_eval_measurement_hypo_hype_3_LSTM_83 = test_eval

train_rmse_measurement_hypo_hype_3_LSTM_83 = train_rmse
val_rmse_measurement_hypo_hype_3_LSTM_83 = val_rmse
test_rmse_measurement_hypo_hype_3_LSTM_83 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_35"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_43 (LSTM)              (None, 8, 32)             4608      
                                                                 
 batch_normalization_22 (Bat  (None, 8, 32)            128       
 chNormalization)                                                
                                                                 
 dropout_22 (Dropout)        (None, 8, 32)             0         
                                                                 
 lstm_44 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_23 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_23 (Dropout)        (None, 64)                0         
                                                                 
 dense_35 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1778/1778 [==============================] - 9s 4ms/step - loss: 35.7242 - val_loss: 3.0762
Epoch 2/20
1778/1778 [==============================] - 8s 4ms/step - loss: 20.6317 - val_loss: 7.3408
Epoch 3/20
1778/1778 [==============================] - 7s 4ms/step - loss: 19.7760 - val_loss: 8.0503
Epoch 4/20
1778/1778 [==============================] - 7s 4ms/step - loss: 20.0242 - val_loss: 5.9338
3556/3556 [==============================] - 4s 1ms/step
445/445 [==============================] - 1s 1ms/step
445/445 [==============================] - 1s 1ms/step
Evaluación Entrenamiento MAE: 8.0759916305542
Evaluación Validación MAE: 5.93383264541626
Evaluación Prueba MAE: 8.28266716003418
Entrenamiento RMSE: 11.11864185333252
Validación RMSE: 6.500219345092773
Prueba RMSE: 8.75619125366211

Resultado obtenido con 3 arquitecturas distintas de la LSTM¶

Después de realizar las pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement' + Hypoglycemia + Hyperglycemia

El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.

Las arquitecturas LSTM utilizadas son:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 64. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • return_sequences: True.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5.
    • units: 64. Número de neuronas en la capa LSTM.
    • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [250]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_1_LSTM_83 vacía
tabla_1_LSTM_83 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_1_LSTM_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_LSTM_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_LSTM_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_1_LSTM_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_LSTM_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_LSTM_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_1_LSTM_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_LSTM_83
tabla_1_LSTM_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_LSTM_83
tabla_1_LSTM_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_LSTM_83

tabla_1_LSTM_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_LSTM_83
tabla_1_LSTM_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_LSTM_83
tabla_1_LSTM_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_LSTM_83

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(2) {
        text-align: center;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_LSTM_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 25.646206
Validación MAE 27.553596
Test MAE 36.111660
Entrenamiento RMSE 30.676008
Validación RMSE 29.090605
Test RMSE 37.675026
In [251]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_LSTM_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_1_LSTM_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_1_LSTM_83 = pd.concat([tabla_1_LSTM_83, pd.DataFrame([row])])

Tabla Arquitectura 2, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [252]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla vacía
tabla_2_LSTM_83 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_2_LSTM_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_LSTM_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_LSTM_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_2_LSTM_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_LSTM_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_LSTM_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_2_LSTM_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_LSTM_83
tabla_2_LSTM_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_LSTM_83
tabla_2_LSTM_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_LSTM_83

tabla_2_LSTM_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_LSTM_83
tabla_2_LSTM_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_LSTM_83
tabla_2_LSTM_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_LSTM_83

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_LSTM_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 2, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 0.862907
Validación MAE 0.610178
Test MAE 0.465580
Entrenamiento RMSE 3.425832
Validación RMSE 1.738084
Test RMSE 1.239297
In [253]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_LSTM_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_2_LSTM_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_2_LSTM_83 = pd.concat([tabla_2_LSTM_83, pd.DataFrame([row])])

Tabla Arquitectura 3, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • return_sequences: True.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5.
  • units: 64. Número de neuronas en la capa LSTM.
  • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [254]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_LSTM_83 vacía
tabla_3_LSTM_83 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_LSTM_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_LSTM_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_LSTM_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_LSTM_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_LSTM_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_LSTM_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_LSTM_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_LSTM_83
tabla_3_LSTM_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_LSTM_83
tabla_3_LSTM_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_LSTM_83

tabla_3_LSTM_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_LSTM_83
tabla_3_LSTM_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_LSTM_83
tabla_3_LSTM_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_LSTM_83

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_LSTM_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 8.075992
Validación MAE 5.933833
Test MAE 8.282667
Entrenamiento RMSE 11.118642
Validación RMSE 6.500219
Test RMSE 8.756191
In [255]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_LSTM_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_3_LSTM_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_3_LSTM_83 = pd.concat([tabla_3_LSTM_83, pd.DataFrame([row])])

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM¶

In [256]:
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_LSTM_83, tabla_2_LSTM_83, tabla_3_LSTM_83], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 25.646206 Measurement + Hypo_Hyper Entrenamiento MAE 0.862907 Measurement + Hypo_Hyper Entrenamiento MAE 8.075992 Measurement + Hypo_Hyper
Validación MAE 27.553596 Measurement + Hypo_Hyper Validación MAE 0.610178 Measurement + Hypo_Hyper Validación MAE 5.933833 Measurement + Hypo_Hyper
Test MAE 36.111660 Measurement + Hypo_Hyper Test MAE 0.465580 Measurement + Hypo_Hyper Test MAE 8.282667 Measurement + Hypo_Hyper
Entrenamiento RMSE 30.676008 Measurement + Hypo_Hyper Entrenamiento RMSE 3.425832 Measurement + Hypo_Hyper Entrenamiento RMSE 11.118642 Measurement + Hypo_Hyper
Validación RMSE 29.090605 Measurement + Hypo_Hyper Validación RMSE 1.738084 Measurement + Hypo_Hyper Validación RMSE 6.500219 Measurement + Hypo_Hyper
Test RMSE 37.675026 Measurement + Hypo_Hyper Test RMSE 1.239297 Measurement + Hypo_Hyper Test RMSE 8.756191 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, LSTM¶

In [257]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = tabla_1_LSTM_83.loc[tabla_1_LSTM_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_1_LSTM_83.loc[tabla_1_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = tabla_1_LSTM_83.loc[tabla_1_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = tabla_2_LSTM_83.loc[tabla_2_LSTM_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_2_LSTM_83.loc[tabla_2_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = tabla_2_LSTM_83.loc[tabla_2_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = tabla_3_LSTM_83.loc[tabla_3_LSTM_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_3_LSTM_83.loc[tabla_3_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = tabla_3_LSTM_83.loc[tabla_3_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
    {'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_83 = styled_result.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_83}</div>'))

RESULTADO A MEJOR ARQUITECTURA, LSTM

ARQUITECTURA 2
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 0.862907 Measurement + Hypo_Hyper 2
Validación MAE 0.610178 Measurement + Hypo_Hyper 2
Test MAE 0.465580 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 3.425832 Measurement + Hypo_Hyper 2
Validación RMSE 1.738084 Measurement + Hypo_Hyper 2
Test RMSE 1.239297 Measurement + Hypo_Hyper 2

Conclusión LSTM¶

La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.

La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:

  1. Puerta de entrada (input gate): Determina qué nueva información debe agregarse al estado actual.
  2. Puerta de olvido (forget gate): Determina qué información antigua debe descartarse del estado actual.
  3. Puerta de salida (output gate): Determina qué parte del estado actual debe emitirse como salida. Además, las puertas contienen funciones de activación.

El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.

En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 2:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • Validación RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjunto de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Evaluación Entrenamiento, Evaluación Validación, Evaluación Test, MSE Entrenamiento, MSE Validación y MSE Test estas evaluaciones determinan que es capaz de realizar un alto entrenamiento, alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.

Redes Neuronales Recurrentes Convolucionales (CRNN)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo CRNN con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.¶

En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hyperglycemia" indica si una medición está por encima del rango normal.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal

Mejor arquitectura 3¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [258]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [259]:
df
Out[259]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-02-21 19:46:00 121 False False
2018-02-21 20:01:00 124 False False
2018-02-21 20:17:00 131 False False
2018-02-21 20:32:00 128 False False
2018-02-21 20:47:00 129 False False
... ... ... ...
2022-03-14 06:45:00 159 False False
2022-03-14 07:00:00 160 False False
2022-03-14 07:15:00 164 False False
2022-03-14 10:00:00 182 False False
2022-03-14 10:45:00 168 False False

9198 rows × 3 columns

2. Dividir el conjunto de datos¶

In [260]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [261]:
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_36"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv1d_9 (Conv1D)           (None, 6, 32)             320       
                                                                 
 batch_normalization_24 (Bat  (None, 6, 32)            128       
 chNormalization)                                                
                                                                 
 max_pooling1d_9 (MaxPooling  (None, 3, 32)            0         
 1D)                                                             
                                                                 
 dropout_24 (Dropout)        (None, 3, 32)             0         
                                                                 
 lstm_45 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_25 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_25 (Dropout)        (None, 64)                0         
                                                                 
 dense_36 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________

4. Entrenar el modelo¶

In [262]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1778/1778 [==============================] - 5s 2ms/step - loss: 33.9017 - val_loss: 12.1354
Epoch 2/20
1778/1778 [==============================] - 4s 2ms/step - loss: 19.9794 - val_loss: 19.2859
Epoch 3/20
1778/1778 [==============================] - 4s 2ms/step - loss: 19.7206 - val_loss: 4.7905
Epoch 4/20
1778/1778 [==============================] - 4s 2ms/step - loss: 19.6063 - val_loss: 7.1944
Epoch 5/20
1778/1778 [==============================] - 4s 2ms/step - loss: 19.4487 - val_loss: 13.1217
Epoch 6/20
1778/1778 [==============================] - 4s 2ms/step - loss: 19.3297 - val_loss: 3.5562
Epoch 7/20
1778/1778 [==============================] - 4s 2ms/step - loss: 19.1056 - val_loss: 9.4604
Epoch 8/20
1778/1778 [==============================] - 4s 2ms/step - loss: 19.0466 - val_loss: 19.4459
Epoch 9/20
1778/1778 [==============================] - 4s 2ms/step - loss: 18.8525 - val_loss: 9.1188

5. Evaluar¶

In [263]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN_83 = train_eval
val_eval_measurement_hypo_hype_3_CRNN_83 = val_eval
test_eval_measurement_hypo_hype_3_CRNN_83 = test_eval

train_rmse_measurement_hypo_hype_3_CRNN_83 = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN_83 = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN_83 = test_rmse
3556/3556 [==============================] - 2s 650us/step
445/445 [==============================] - 0s 654us/step
445/445 [==============================] - 0s 667us/step
Evaluación Entrenamiento MAE: 11.620800971984863
Evaluación Validación MAE: 9.118827819824219
Evaluación Prueba MAE: 12.937151908874512
Entrenamiento RMSE: 18.15708351135254
Validación RMSE: 10.958942413330078
Prueba RMSE: 14.02910327911377

6. Gráfica¶

In [264]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [265]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step

                     Datos reales  Datos predichos
date                                              
2022-03-14 11:00:00           NaN       188.401703
2022-03-14 11:15:00           NaN       192.112320

Distintas arquitecturas CRNN probadas¶

1º Optimización de hiperparámetros¶

In [266]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_2_CRNN_83 = train_eval
val_eval_measurement_hypo_hype_2_CRNN_83 = val_eval
test_eval_measurement_hypo_hype_2_CRNN_83 = test_eval

train_rmse_measurement_hypo_hype_2_CRNN_83 = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN_83 = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN_83 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1778/1778 [==============================] - 4s 2ms/step - loss: 121.2205 - val_loss: 108.6773
Epoch 2/20
1778/1778 [==============================] - 3s 1ms/step - loss: 76.5391 - val_loss: 64.7054
Epoch 3/20
1778/1778 [==============================] - 3s 2ms/step - loss: 56.9476 - val_loss: 49.4607
Epoch 4/20
1778/1778 [==============================] - 3s 2ms/step - loss: 52.9150 - val_loss: 48.6064
Epoch 5/20
1778/1778 [==============================] - 3s 2ms/step - loss: 55.8967 - val_loss: 55.9629
Epoch 6/20
1778/1778 [==============================] - 3s 2ms/step - loss: 56.1408 - val_loss: 57.0564
Epoch 7/20
1778/1778 [==============================] - 3s 2ms/step - loss: 58.9478 - val_loss: 62.0105
3556/3556 [==============================] - 2s 564us/step
445/445 [==============================] - 0s 582us/step
445/445 [==============================] - 0s 585us/step
Evaluación Entrenamiento MAE: 53.23556900024414
Evaluación Validación MAE: 62.01028060913086
Evaluación Prueba MAE: 70.5667953491211
Entrenamiento RMSE: 57.698856353759766
Validación RMSE: 62.708290100097656
Prueba RMSE: 71.37937927246094

2º Optimización de hiperparámetros¶

In [267]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_1_CRNN_83 = train_eval
val_eval_measurement_hypo_hype_1_CRNN_83 = val_eval
test_eval_measurement_hypo_hype_1_CRNN_83 = test_eval

train_rmse_measurement_hypo_hype_1_CRNN_83 = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN_83 = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN_83 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1778/1778 [==============================] - 3s 2ms/step - loss: 120.6078 - val_loss: 107.7612
Epoch 2/20
1778/1778 [==============================] - 2s 1ms/step - loss: 73.8998 - val_loss: 60.6866
Epoch 3/20
1778/1778 [==============================] - 2s 1ms/step - loss: 48.1719 - val_loss: 37.4952
Epoch 4/20
1778/1778 [==============================] - 2s 1ms/step - loss: 43.6322 - val_loss: 37.3698
Epoch 5/20
1778/1778 [==============================] - 3s 1ms/step - loss: 53.7111 - val_loss: 52.6472
Epoch 6/20
1778/1778 [==============================] - 2s 1ms/step - loss: 51.1199 - val_loss: 50.6407
Epoch 7/20
1778/1778 [==============================] - 2s 1ms/step - loss: 58.1492 - val_loss: 60.7080
3556/3556 [==============================] - 2s 539us/step
445/445 [==============================] - 0s 542us/step
445/445 [==============================] - 0s 527us/step
Evaluación Entrenamiento MAE: 52.06108093261719
Evaluación Validación MAE: 60.70766830444336
Evaluación Prueba MAE: 69.26417541503906
Entrenamiento RMSE: 56.53105163574219
Validación RMSE: 61.42062759399414
Prueba RMSE: 70.09201049804688

Resultado obtenido con 3 arquitecturas distintas de la CRNN¶

Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement' + Hypoglycemia + Hyperglycemia

El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.

Las arquitecturas CRNN utilizadas son:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 64. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [268]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_1_CRNN vacía
tabla_1_CRNN_83 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_1_CRNN_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_1_CRNN_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_1_CRNN_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN_83
tabla_1_CRNN_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN_83
tabla_1_CRNN_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN_83

tabla_1_CRNN_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN_83
tabla_1_CRNN_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN_83
tabla_1_CRNN_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN_83

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(2) {
        text-align: center;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 52.061081
Validación MAE 60.707668
Test MAE 69.264175
Entrenamiento RMSE 56.531052
Validación RMSE 61.420628
Test RMSE 70.092010
In [269]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_1_CRNN_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_1_CRNN_83 = pd.concat([tabla_1_CRNN_83, pd.DataFrame([row])])

Tabla Arquitectura 2, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 64. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [270]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_2_CRNN vacía
tabla_2_CRNN_83 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_2_CRNN_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_2_CRNN_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_2_CRNN_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN_83
tabla_2_CRNN_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN_83
tabla_2_CRNN_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN_83

tabla_2_CRNN_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN_83
tabla_2_CRNN_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN_83
tabla_2_CRNN_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN_83

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 2, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 53.235569
Validación MAE 62.010281
Test MAE 70.566795
Entrenamiento RMSE 57.698856
Validación RMSE 62.708290
Test RMSE 71.379379
In [271]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_2_CRNN_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_2_CRNN_83 = pd.concat([tabla_2_CRNN_83, pd.DataFrame([row])])

Tabla Arquitectura 3, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [272]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_83 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_CRNN_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_CRNN_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_CRNN_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_83
tabla_3_CRNN_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_83
tabla_3_CRNN_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_83

tabla_3_CRNN_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_83
tabla_3_CRNN_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_83
tabla_3_CRNN_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_83

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 11.620801
Validación MAE 9.118828
Test MAE 12.937152
Entrenamiento RMSE 18.157084
Validación RMSE 10.958942
Test RMSE 14.029103
In [273]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_3_CRNN_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_3_CRNN_83 = pd.concat([tabla_3_CRNN_83, pd.DataFrame([row])])

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN¶

In [274]:
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN_83, tabla_2_CRNN_83, tabla_3_CRNN_83], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 52.061081 Measurement + Hypo_Hyper Entrenamiento MAE 53.235569 Measurement + Hypo_Hyper Entrenamiento MAE 11.620801 Measurement + Hypo_Hyper
Validación MAE 60.707668 Measurement + Hypo_Hyper Validación MAE 62.010281 Measurement + Hypo_Hyper Validación MAE 9.118828 Measurement + Hypo_Hyper
Test MAE 69.264175 Measurement + Hypo_Hyper Test MAE 70.566795 Measurement + Hypo_Hyper Test MAE 12.937152 Measurement + Hypo_Hyper
Entrenamiento RMSE 56.531052 Measurement + Hypo_Hyper Entrenamiento RMSE 57.698856 Measurement + Hypo_Hyper Entrenamiento RMSE 18.157084 Measurement + Hypo_Hyper
Validación RMSE 61.420628 Measurement + Hypo_Hyper Validación RMSE 62.708290 Measurement + Hypo_Hyper Validación RMSE 10.958942 Measurement + Hypo_Hyper
Test RMSE 70.092010 Measurement + Hypo_Hyper Test RMSE 71.379379 Measurement + Hypo_Hyper Test RMSE 14.029103 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, CRNN¶

In [275]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = tabla_1_CRNN_83.loc[tabla_1_CRNN_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_1_CRNN_83.loc[tabla_1_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = tabla_1_CRNN_83.loc[tabla_1_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = tabla_2_CRNN_83.loc[tabla_2_CRNN_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_2_CRNN_83.loc[tabla_2_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = tabla_2_CRNN_83.loc[tabla_2_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = tabla_3_CRNN_83.loc[tabla_3_CRNN_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_3_CRNN_83.loc[tabla_3_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = tabla_3_CRNN_83.loc[tabla_3_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
    {'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
CRNN_83 = styled_result.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_83}</div>'))

RESULTADO A MEJOR ARQUITECTURA, CRNN

ARQUITECTURA 3
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 11.620801 Measurement + Hypo_Hyper 3
Validación MAE 9.118828 Measurement + Hypo_Hyper 3
Test MAE 12.937152 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 18.157084 Measurement + Hypo_Hyper 3
Validación RMSE 10.958942 Measurement + Hypo_Hyper 3
Test RMSE 14.029103 Measurement + Hypo_Hyper 3

Conclusión CRNN¶

La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.

Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.

La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.

En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 3:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos.
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Validación RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjunto de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Entrenamiento MAE, Validación MAE, Test MAE, Entrenamiento RMSE, Validación RMSE y Test RMSE, estas evaluaciones determinan que es capaz de realizar un alto entrenamiento, alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.

Comparativa de resultados LSTM y CRNN¶

In [276]:
from IPython.display import display, HTML

html_tables_83 = f"""
<style>
    .table-wrapper {{
        display: inline-block;
        font-size: 14px;
        vertical-align: top;
        margin-right: 20px;
    }}
    .table-wrapper table {{
        font-size: 14px;
    }}
    .table-wrapper th, .table-wrapper td {{
        width: 100px;
    }}
    .table-wrapper h3 {{
        text-align: center;
        font-size: 18px;
        color: blue;
    }}
    h2 {{
        text-align: center;
    }}
</style>
<h2>Paciente 83</h2>
<div class="table-wrapper">
    <h3>LSTM</h3>
    {LSTM_83}
</div>
<div class="table-wrapper">
    <h3>CRNN</h3>
    {CRNN_83}
</div>
"""

display(HTML(html_tables_83))

Paciente 83

LSTM

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 0.862907 Measurement + Hypo_Hyper 2
Validación MAE 0.610178 Measurement + Hypo_Hyper 2
Test MAE 0.465580 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 3.425832 Measurement + Hypo_Hyper 2
Validación RMSE 1.738084 Measurement + Hypo_Hyper 2
Test RMSE 1.239297 Measurement + Hypo_Hyper 2

CRNN

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 11.620801 Measurement + Hypo_Hyper 3
Validación MAE 9.118828 Measurement + Hypo_Hyper 3
Test MAE 12.937152 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 18.157084 Measurement + Hypo_Hyper 3
Validación RMSE 10.958942 Measurement + Hypo_Hyper 3
Test RMSE 14.029103 Measurement + Hypo_Hyper 3

Mejor Modelo (Paciente 83)¶

In [277]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN_83': 1, 'tabla_2_CRNN_83': 2, 'tabla_3_CRNN_83': 3}
tables_arch2 = {'tabla_1_LSTM_83': 1, 'tabla_2_LSTM_83': 2, 'tabla_3_LSTM_83': 3}

# Para cada fila
for row in rows:
    min_vals = []
    
    # Arquitectura 1
    for table_name, arch in tables_arch1.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'CRNN', arch))

    # Arquitectura 2
    for table_name, arch in tables_arch2.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'LSTM', arch))

    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])

    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')

    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [min_arch],
        'Arquitectura (Hiperparámetros)': [min_num]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 83)</span></center>"
display(HTML(html_text))

# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()

# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))

# Nombrando
styled_result4 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result4 = styled_result4.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_83.style.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"

# Muestra el HTML
display(HTML(html))

# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))


# Mostrar la figura
display(fig_hypo_hype_2_LSTM_83)

# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_83.style.set_properties(**{'text-align': 'center'}).hide(axis='index')

# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"

# Mostrar la tabla centrada
display(HTML(table_html))

MEJOR ARQUITECTURA (Paciente 83)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 0.862907 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.610178 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.465580 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 3.425832 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 1.738084 Measurement + Hypo_Hyper LSTM 2
Test RMSE 1.239297 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-03-14 11:00:00 nan 166.793915
2022-03-14 11:15:00 nan 165.903671

Precisión del modelo con Clarke Error Grid

Zona Conteo Proporción
A 14216 100.00%
B 0 0.00%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Conclusión Paciente 83¶

Los resultados nos aclara que la mejor opción para el paciente 83 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.

Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

La conclusión final para el paciente 83 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hypo_Hyper para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.

Paciente 92
¶

La comparativa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 92 debido a que tras un análisis extenso se ha comprobado que es el 5º con mayor número de mediciones.

Se va a llevar a cabo la comparativa de soluciones con los modelos RNN y CRNN pero con la mejor solución ya obtenida con el paciente 22. De esta manera se va a trabajar con la mejor arquitectura de hiperparámetros y de características de entrada así se va a poder confirmar que el experimento se ha realizado con éxito.

Las 3 arquitecturas de hiperparámetros de LSTM y CRNN son:

Arquitectura de hiperparámetros de LSTM:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 64. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • return_sequences: True.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5.
    • units: 64. Número de neuronas en la capa LSTM.
    • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Arquitectura de hiperparámetros de CRNN:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

La característica de entrada va a ser: 'Measurement + Hypo_Hyper'

In [278]:
# Filtrar los datos para el paciente 92
df_paciente_92 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 92]

# Imprimir los resultados
df_paciente_92
Out[278]:
Patient_ID Measurement_date Measurement_time Measurement In_Range Hypoglycemia Hyperglycemia Hour_of_Day Day_of_Week
Datetime
2018-06-06 11:30:00 92 2018-06-06 1900-01-01 11:38:00 372 False False True 11 2
2018-06-06 11:45:00 92 2018-06-06 1900-01-01 11:53:00 365 False False True 11 2
2018-06-06 12:00:00 92 2018-06-06 1900-01-01 12:08:00 345 False False True 12 2
2018-06-06 12:15:00 92 2018-06-06 1900-01-01 12:23:00 327 False False True 12 2
2018-06-06 12:30:00 92 2018-06-06 1900-01-01 12:38:00 305 False False True 12 2
... ... ... ... ... ... ... ... ... ...
2022-01-10 12:00:00 92 2022-01-10 1900-01-01 12:00:00 177 True False False 12 0
2022-01-10 13:15:00 92 2022-01-10 1900-01-01 13:15:00 174 True False False 13 0
2022-01-10 15:00:00 92 2022-01-10 1900-01-01 15:00:00 186 True False False 15 0
2022-01-10 15:45:00 92 2022-01-10 1900-01-01 15:45:00 190 False False True 15 0
2022-01-10 16:00:00 92 2022-01-10 1900-01-01 16:00:00 193 False False True 16 0

8509 rows × 9 columns

Red Neuronal Recurrente (RNN) con el algoritmo Long Short-Term Memory (LSTM)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo LSTM con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'¶

En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal
  • "Hyperglycemia" indica si una medición está por encima del rango normal.

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [279]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [280]:
df
Out[280]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-06-06 11:38:00 372 False True
2018-06-06 11:53:00 365 False True
2018-06-06 12:08:00 345 False True
2018-06-06 12:23:00 327 False True
2018-06-06 12:38:00 305 False True
... ... ... ...
2022-01-10 12:00:00 177 False False
2022-01-10 13:15:00 174 False False
2022-01-10 15:00:00 186 False False
2022-01-10 15:45:00 190 False True
2022-01-10 16:00:00 193 False True

8509 rows × 3 columns

2. Dividir el conjunto de datos¶

In [281]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [282]:
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))  # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_39"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_48 (LSTM)              (None, 64)                17408     
                                                                 
 dense_39 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________

4. Entrenar el modelo¶

In [283]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1577/1577 [==============================] - 5s 3ms/step - loss: 2.9617 - val_loss: 0.7359
Epoch 2/20
1577/1577 [==============================] - 5s 3ms/step - loss: 2.6971 - val_loss: 0.2157
Epoch 3/20
1577/1577 [==============================] - 5s 3ms/step - loss: 2.3773 - val_loss: 0.7206
Epoch 4/20
1577/1577 [==============================] - 5s 3ms/step - loss: 2.0849 - val_loss: 0.1924
Epoch 5/20
1577/1577 [==============================] - 5s 3ms/step - loss: 1.9256 - val_loss: 0.7419
Epoch 6/20
1577/1577 [==============================] - 5s 3ms/step - loss: 1.9050 - val_loss: 1.4811
Epoch 7/20
1577/1577 [==============================] - 5s 3ms/step - loss: 1.9441 - val_loss: 0.6038

5. Evaluar¶

In [284]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2_LSTM_92 = train_eval
val_eval_measurement_hypo_hype_2_LSTM_92 = val_eval
test_eval_measurement_hypo_hype_2_LSTM_92 = test_eval

train_rmse_measurement_hypo_hype_2_LSTM_92 = train_rmse
val_rmse_measurement_hypo_hype_2_LSTM_92 = val_rmse
test_rmse_measurement_hypo_hype_2_LSTM_92 = test_rmse
3154/3154 [==============================] - 3s 862us/step
394/394 [==============================] - 0s 839us/step
394/394 [==============================] - 0s 887us/step
Evaluación Entrenamiento MAE: 1.679124116897583
Evaluación Validación MAE: 0.6037915945053101
Evaluación Prueba MAE: 0.6461104154586792
Entrenamiento RMSE: 5.643665313720703
Validación RMSE: 0.7743025422096252
Prueba RMSE: 1.2647500038146973

6. Gráfica¶

In [285]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [286]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')

# Almacenar dato
future_predictions_hypo_hype_2_LSTM_92 = future_predictions

plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step

                     Datos reales  Datos predichos
date                                              
2022-01-10 16:15:00           NaN       186.222000
2022-01-10 16:30:00           NaN       185.486877

6.2 Clarke Error Grid¶

In [287]:
def clarke_error_grid(ref_values, pred_values):
    assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))

    if max(ref_values) > 400 or max(pred_values) > 400:
        print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
    if min(ref_values) < 0 or min(pred_values) < 0:
        print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values),  min(pred_values)))

    plt.clf()
    plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
    plt.title("Clarke Error Grid")
    plt.xlabel("Reference Concentration (mg/dl)")
    plt.ylabel("Prediction Concentration (mg/dl)")
    plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
    plt.gca().set_facecolor('white')
    plt.gca().set_xlim([0, 400])
    plt.gca().set_ylim([0, 400])
    plt.gca().set_aspect((400)/(400))
    plt.plot([0, 400], [0, 400], ':', c='black')
    plt.plot([0, 175/3], [70, 70], '-', c='black')
    plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
    plt.plot([70, 70], [84, 400],'-', c='black')
    plt.plot([0, 70], [180, 180], '-', c='black')
    plt.plot([70, 290], [180, 400],'-', c='black')
    plt.plot([70, 70], [0, 56], '-', c='black')
    plt.plot([70, 400], [56, 320],'-', c='black')
    plt.plot([180, 180], [0, 70], '-', c='black')
    plt.plot([180, 400], [70, 70], '-', c='black')
    plt.plot([240, 240], [70, 180],'-', c='black')
    plt.plot([240, 400], [180, 180], '-', c='black')
    plt.plot([130, 180], [0, 70], '-', c='black')
    plt.text(30, 15, "A", fontsize=15)
    plt.text(370, 260, "B", fontsize=15)
    plt.text(280, 370, "B", fontsize=15)
    plt.text(160, 370, "C", fontsize=15)
    plt.text(160, 15, "C", fontsize=15)
    plt.text(30, 140, "D", fontsize=15)
    plt.text(370, 120, "D", fontsize=15)
    plt.text(30, 370, "E", fontsize=15)
    plt.text(370, 15, "E", fontsize=15)

    zone = [0] * 5
    for i in range(len(ref_values)):
        if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
            zone[0] += 1  # Zone A
        elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
            zone[4] += 1  # Zone E
        elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
            zone[2] += 1  # Zone C
        elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
            zone[3] += 1  # Zone D
        else:
            zone[1] += 1  # Zone B

    return plt, zone

ref_values = y_test
pred_values = y_pred_test

plt, zone = clarke_error_grid(ref_values, pred_values)

# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_92 = plt.gcf()

# Mostrar la figura
plt.show()

# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})

# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total

# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))

# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_92 = zone_df

# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_92.style.hide(axis="index"))
Conteo de Zonas:
Zona Conteo Proporción
A 12607 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Distintas arquitecturas LSTM probadas¶

1º Optimización de hiperparámetros¶

In [288]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')


# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1_LSTM_92 = train_eval
val_eval_measurement_hypo_hype_1_LSTM_92 = val_eval
test_eval_measurement_hypo_hype_1_LSTM_92 = test_eval

train_rmse_measurement_hypo_hype_1_LSTM_92 = train_rmse
val_rmse_measurement_hypo_hype_1_LSTM_92 = val_rmse
test_rmse_measurement_hypo_hype_1_LSTM_92 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_40"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_49 (LSTM)              (None, 32)                4608      
                                                                 
 dense_40 (Dense)            (None, 1)                 33        
                                                                 
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1577/1577 [==============================] - 4s 2ms/step - loss: 141.0571 - val_loss: 129.8837
Epoch 2/20
1577/1577 [==============================] - 3s 2ms/step - loss: 99.4321 - val_loss: 84.2195
Epoch 3/20
1577/1577 [==============================] - 3s 2ms/step - loss: 61.1622 - val_loss: 46.2528
Epoch 4/20
1577/1577 [==============================] - 3s 2ms/step - loss: 32.1005 - val_loss: 20.4414
Epoch 5/20
1577/1577 [==============================] - 3s 2ms/step - loss: 20.0603 - val_loss: 13.3125
Epoch 6/20
1577/1577 [==============================] - 3s 2ms/step - loss: 13.3559 - val_loss: 10.4139
Epoch 7/20
1577/1577 [==============================] - 3s 2ms/step - loss: 6.8749 - val_loss: 8.3733
Epoch 8/20
1577/1577 [==============================] - 3s 2ms/step - loss: 4.9976 - val_loss: 5.2236
Epoch 9/20
1577/1577 [==============================] - 3s 2ms/step - loss: 3.8893 - val_loss: 3.1057
Epoch 10/20
1577/1577 [==============================] - 3s 2ms/step - loss: 3.0643 - val_loss: 1.0371
Epoch 11/20
1577/1577 [==============================] - 3s 2ms/step - loss: 2.2475 - val_loss: 1.1954
Epoch 12/20
1577/1577 [==============================] - 3s 2ms/step - loss: 2.2237 - val_loss: 1.1394
Epoch 13/20
1577/1577 [==============================] - 3s 2ms/step - loss: 2.1907 - val_loss: 1.5419
3154/3154 [==============================] - 2s 609us/step
394/394 [==============================] - 0s 620us/step
394/394 [==============================] - 0s 637us/step
Evaluación Entrenamiento MAE: 4.471574783325195
Evaluación Validación MAE: 1.5418845415115356
Evaluación Prueba MAE: 2.2097952365875244
Entrenamiento RMSE: 8.106847763061523
Validación RMSE: 1.9643903970718384
Prueba RMSE: 3.0689289569854736

2º Optimización de hiperparámetros¶

In [289]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001))) 
model.add(BatchNormalization())
model.add(Dropout(0.5))  # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_LSTM_92 = train_eval
val_eval_measurement_hypo_hype_3_LSTM_92 = val_eval
test_eval_measurement_hypo_hype_3_LSTM_92 = test_eval

train_rmse_measurement_hypo_hype_3_LSTM_92 = train_rmse
val_rmse_measurement_hypo_hype_3_LSTM_92 = val_rmse
test_rmse_measurement_hypo_hype_3_LSTM_92 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_41"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 lstm_50 (LSTM)              (None, 8, 32)             4608      
                                                                 
 batch_normalization_26 (Bat  (None, 8, 32)            128       
 chNormalization)                                                
                                                                 
 dropout_26 (Dropout)        (None, 8, 32)             0         
                                                                 
 lstm_51 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_27 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_27 (Dropout)        (None, 64)                0         
                                                                 
 dense_41 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1577/1577 [==============================] - 7s 4ms/step - loss: 45.2399 - val_loss: 12.0705
Epoch 2/20
1577/1577 [==============================] - 6s 4ms/step - loss: 26.0424 - val_loss: 5.7474
Epoch 3/20
1577/1577 [==============================] - 6s 4ms/step - loss: 25.5011 - val_loss: 5.9949
Epoch 4/20
1577/1577 [==============================] - 6s 4ms/step - loss: 25.3872 - val_loss: 7.8347
Epoch 5/20
1577/1577 [==============================] - 6s 4ms/step - loss: 25.2674 - val_loss: 2.7759
Epoch 6/20
1577/1577 [==============================] - 6s 4ms/step - loss: 24.9938 - val_loss: 14.1727
Epoch 7/20
1577/1577 [==============================] - 7s 4ms/step - loss: 24.9557 - val_loss: 16.8441
Epoch 8/20
1577/1577 [==============================] - 6s 4ms/step - loss: 24.6701 - val_loss: 11.9341
3154/3154 [==============================] - 4s 1ms/step
394/394 [==============================] - 0s 1ms/step
394/394 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 12.527313232421875
Evaluación Validación MAE: 11.934123039245605
Evaluación Prueba MAE: 14.363471984863281
Entrenamiento RMSE: 15.320870399475098
Validación RMSE: 13.936296463012695
Prueba RMSE: 15.432523727416992

Resultado obtenido con 3 arquitecturas distintas de la LSTM¶

Después de realizar las pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement' + Hypoglycemia + Hyperglycemia

El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.

Las arquitecturas LSTM utilizadas son:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 64. Número de neuronas en la capa LSTM.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units: 32. Número de neuronas en la capa LSTM.
    • return_sequences: True.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5.
    • units: 64. Número de neuronas en la capa LSTM.
    • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
    • activation: 'relu'. Función de activación utilizada en la capa LSTM.
    • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [290]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_1_LSTM_92 vacía
tabla_1_LSTM_92 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_1_LSTM_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_LSTM_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_LSTM_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_1_LSTM_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_LSTM_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_LSTM_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_1_LSTM_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_LSTM_92
tabla_1_LSTM_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_LSTM_92
tabla_1_LSTM_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_LSTM_92

tabla_1_LSTM_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_LSTM_92
tabla_1_LSTM_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_LSTM_92
tabla_1_LSTM_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_LSTM_92

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(2) {
        text-align: center;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_LSTM_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 4.471575
Validación MAE 1.541885
Test MAE 2.209795
Entrenamiento RMSE 8.106848
Validación RMSE 1.964390
Test RMSE 3.068929
In [291]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_LSTM_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_1_LSTM_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_1_LSTM_92 = pd.concat([tabla_1_LSTM_92, pd.DataFrame([row])])

Tabla Arquitectura 2, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [292]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla vacía
tabla_2_LSTM_92 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_2_LSTM_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_LSTM_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_LSTM_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_2_LSTM_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_LSTM_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_LSTM_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_2_LSTM_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_LSTM_92
tabla_2_LSTM_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_LSTM_92
tabla_2_LSTM_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_LSTM_92

tabla_2_LSTM_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_LSTM_92
tabla_2_LSTM_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_LSTM_92
tabla_2_LSTM_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_LSTM_92

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_LSTM_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 2, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 1.679124
Validación MAE 0.603792
Test MAE 0.646110
Entrenamiento RMSE 5.643665
Validación RMSE 0.774303
Test RMSE 1.264750
In [293]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_LSTM_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_2_LSTM_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_2_LSTM_92 = pd.concat([tabla_2_LSTM_92, pd.DataFrame([row])])

Tabla Arquitectura 3, LSTM¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 32. Número de neuronas en la capa LSTM.
  • return_sequences: True.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5.
  • units: 64. Número de neuronas en la capa LSTM.
  • return_sequences: False. True/False. Determina si una capa LSTM devuelve la secuencia completa de salidas o solo la última; 'True' devuelve toda la secuencia, 'False' solo la última salida.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • kernel_regularizer: L1L2 (0.001 y 0.001). Aplica penalizaciones a los coeficientes de la capa durante el entrenamiento, en este caso utilizando la regularización L1 y L2, lo que puede ayudar a prevenir el sobreajuste.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [294]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_LSTM_92 vacía
tabla_3_LSTM_92 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_LSTM_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_LSTM_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_LSTM_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_LSTM_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_LSTM_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_LSTM_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_LSTM_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_LSTM_92
tabla_3_LSTM_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_LSTM_92
tabla_3_LSTM_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_LSTM_92

tabla_3_LSTM_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_LSTM_92
tabla_3_LSTM_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_LSTM_92
tabla_3_LSTM_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_LSTM_92

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_LSTM_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, LSTM
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 12.527313
Validación MAE 11.934123
Test MAE 14.363472
Entrenamiento RMSE 15.320870
Validación RMSE 13.936296
Test RMSE 15.432524
In [295]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_LSTM_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_3_LSTM_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_3_LSTM_92 = pd.concat([tabla_3_LSTM_92, pd.DataFrame([row])])

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM¶

In [296]:
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_LSTM_92, tabla_2_LSTM_92, tabla_3_LSTM_92], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 4.471575 Measurement + Hypo_Hyper Entrenamiento MAE 1.679124 Measurement + Hypo_Hyper Entrenamiento MAE 12.527313 Measurement + Hypo_Hyper
Validación MAE 1.541885 Measurement + Hypo_Hyper Validación MAE 0.603792 Measurement + Hypo_Hyper Validación MAE 11.934123 Measurement + Hypo_Hyper
Test MAE 2.209795 Measurement + Hypo_Hyper Test MAE 0.646110 Measurement + Hypo_Hyper Test MAE 14.363472 Measurement + Hypo_Hyper
Entrenamiento RMSE 8.106848 Measurement + Hypo_Hyper Entrenamiento RMSE 5.643665 Measurement + Hypo_Hyper Entrenamiento RMSE 15.320870 Measurement + Hypo_Hyper
Validación RMSE 1.964390 Measurement + Hypo_Hyper Validación RMSE 0.774303 Measurement + Hypo_Hyper Validación RMSE 13.936296 Measurement + Hypo_Hyper
Test RMSE 3.068929 Measurement + Hypo_Hyper Test RMSE 1.264750 Measurement + Hypo_Hyper Test RMSE 15.432524 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, LSTM¶

In [297]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = tabla_1_LSTM_92.loc[tabla_1_LSTM_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_1_LSTM_92.loc[tabla_1_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = tabla_1_LSTM_92.loc[tabla_1_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = tabla_2_LSTM_92.loc[tabla_2_LSTM_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_2_LSTM_92.loc[tabla_2_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = tabla_2_LSTM_92.loc[tabla_2_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = tabla_3_LSTM_92.loc[tabla_3_LSTM_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_3_LSTM_92.loc[tabla_3_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = tabla_3_LSTM_92.loc[tabla_3_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
    {'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_92 = styled_result.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_92}</div>'))

RESULTADO A MEJOR ARQUITECTURA, LSTM

ARQUITECTURA 2
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 1.679124 Measurement + Hypo_Hyper 2
Validación MAE 0.603792 Measurement + Hypo_Hyper 2
Test MAE 0.646110 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 5.643665 Measurement + Hypo_Hyper 2
Validación RMSE 0.774303 Measurement + Hypo_Hyper 2
Test RMSE 1.264750 Measurement + Hypo_Hyper 2

Conclusión LSTM¶

La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.

La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:

  1. Puerta de entrada (input gate): Determina qué nueva información debe agregarse al estado actual.
  2. Puerta de olvido (forget gate): Determina qué información antigua debe descartarse del estado actual.
  3. Puerta de salida (output gate): Determina qué parte del estado actual debe emitirse como salida. Además, las puertas contienen funciones de activación.

El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.

En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 2:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Validación RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjunto de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Entrenamiento MAE, Validación MAE, Test MAE, Entrenamiento RMSE, Validación RMSE y Test RMSE, estas evaluaciones determinan que es capaz de realizar un alto entrenamiento y buena generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con LSTM con las características de entrada Measurement + Hypo_Hyper.

Redes Neuronales Recurrentes Convolucionales (CRNN)¶

A continuación se realiza los pasos correspondientes para su entrenamiento.

Algoritmo CRNN con características de entrada: 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.¶

En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.

  • 'Mesurement' es el nivel de glucosa.
  • "Hyperglycemia" indica si una medición está por encima del rango normal.
  • "Hypoglycemia" indica si una medición está por debajo del rango normal

Mejor arquitectura 2¶

Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 64. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

1. Preprocesamiento de los datos¶

In [298]:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten

# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
In [299]:
df
Out[299]:
Measurement Hypoglycemia Hyperglycemia
Datetime
2018-06-06 11:38:00 372 False True
2018-06-06 11:53:00 365 False True
2018-06-06 12:08:00 345 False True
2018-06-06 12:23:00 327 False True
2018-06-06 12:38:00 305 False True
... ... ... ...
2022-01-10 12:00:00 177 False False
2022-01-10 13:15:00 174 False False
2022-01-10 15:00:00 186 False False
2022-01-10 15:45:00 190 False True
2022-01-10 16:00:00 193 False True

8509 rows × 3 columns

2. Dividir el conjunto de datos¶

In [300]:
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

3. Definir arquitectura del modelo¶

In [301]:
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()

# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_42"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv1d_12 (Conv1D)          (None, 6, 32)             320       
                                                                 
 batch_normalization_28 (Bat  (None, 6, 32)            128       
 chNormalization)                                                
                                                                 
 max_pooling1d_12 (MaxPoolin  (None, 3, 32)            0         
 g1D)                                                            
                                                                 
 dropout_28 (Dropout)        (None, 3, 32)             0         
                                                                 
 lstm_52 (LSTM)              (None, 64)                24832     
                                                                 
 batch_normalization_29 (Bat  (None, 64)               256       
 chNormalization)                                                
                                                                 
 dropout_29 (Dropout)        (None, 64)                0         
                                                                 
 dense_42 (Dense)            (None, 1)                 65        
                                                                 
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________

4. Entrenar el modelo¶

In [302]:
# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)
Epoch 1/20
1577/1577 [==============================] - 5s 2ms/step - loss: 44.1603 - val_loss: 19.9735
Epoch 2/20
1577/1577 [==============================] - 3s 2ms/step - loss: 25.6931 - val_loss: 18.6398
Epoch 3/20
1577/1577 [==============================] - 3s 2ms/step - loss: 25.3511 - val_loss: 11.5467
Epoch 4/20
1577/1577 [==============================] - 3s 2ms/step - loss: 24.9358 - val_loss: 21.7919
Epoch 5/20
1577/1577 [==============================] - 3s 2ms/step - loss: 24.9161 - val_loss: 21.1320
Epoch 6/20
1577/1577 [==============================] - 3s 2ms/step - loss: 24.8354 - val_loss: 12.7428

5. Evaluar¶

In [303]:
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN_92 = train_eval
val_eval_measurement_hypo_hype_3_CRNN_92 = val_eval
test_eval_measurement_hypo_hype_3_CRNN_92 = test_eval

train_rmse_measurement_hypo_hype_3_CRNN_92 = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN_92 = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN_92 = test_rmse
3154/3154 [==============================] - 2s 648us/step
394/394 [==============================] - 0s 657us/step
394/394 [==============================] - 0s 716us/step
Evaluación Entrenamiento MAE: 23.519376754760742
Evaluación Validación MAE: 12.742785453796387
Evaluación Prueba MAE: 10.551319122314453
Entrenamiento RMSE: 26.978843688964844
Validación RMSE: 15.696503639221191
Prueba RMSE: 14.015483856201172

6. Gráfica¶

In [304]:
# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()

6.1 Gráfica con datos originales¶

In [305]:
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]

real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])

# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos   
    next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
    predictions_15min.append(next_prediction)
    last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica

# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]

# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)

results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])

# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()

print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step

                     Datos reales  Datos predichos
date                                              
2022-01-10 16:15:00           NaN       169.396500
2022-01-10 16:30:00           NaN       169.985672

Distintas arquitecturas CRNN probadas¶

1º Optimización de hiperparámetros¶

In [306]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')

# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_2_CRNN_92 = train_eval
val_eval_measurement_hypo_hype_2_CRNN_92 = val_eval
test_eval_measurement_hypo_hype_2_CRNN_92 = test_eval

train_rmse_measurement_hypo_hype_2_CRNN_92 = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN_92 = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN_92 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1577/1577 [==============================] - 3s 2ms/step - loss: 139.1703 - val_loss: 126.4468
Epoch 2/20
1577/1577 [==============================] - 2s 1ms/step - loss: 102.6864 - val_loss: 90.3819
Epoch 3/20
1577/1577 [==============================] - 2s 1ms/step - loss: 93.2370 - val_loss: 84.4903
Epoch 4/20
1577/1577 [==============================] - 2s 1ms/step - loss: 81.4039 - val_loss: 74.3690
Epoch 5/20
1577/1577 [==============================] - 2s 1ms/step - loss: 73.7009 - val_loss: 72.1209
Epoch 6/20
1577/1577 [==============================] - 2s 1ms/step - loss: 70.5329 - val_loss: 72.3251
Epoch 7/20
1577/1577 [==============================] - 2s 1ms/step - loss: 78.3239 - val_loss: 72.9205
Epoch 8/20
1577/1577 [==============================] - 2s 1ms/step - loss: 79.2571 - val_loss: 74.0319
3154/3154 [==============================] - 2s 552us/step
394/394 [==============================] - 0s 564us/step
394/394 [==============================] - 0s 582us/step
Evaluación Entrenamiento MAE: 69.35159301757812
Evaluación Validación MAE: 74.0319595336914
Evaluación Prueba MAE: 79.8343276977539
Entrenamiento RMSE: 76.4446029663086
Validación RMSE: 75.49947357177734
Prueba RMSE: 81.06375885009766

2º Optimización de hiperparámetros¶

In [307]:
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()

# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))

# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)

# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])

# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()

# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()

# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
    df_resampled = df.fillna(method='ffill')
    return df_resampled

# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)

# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"

# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]

# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
    
    # Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).    
    xs = []
    ys = []
    
    # Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
    for i in range(len(data)-seq_length-1):
        x = data.iloc[i:(i+seq_length)]
        y = data.iloc[i+seq_length]['Measurement']
        xs.append(x)
        ys.append(y)
    return np.array(xs), np.array(ys)

# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces

# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)

# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)

# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))

# 15. Tamaño del batch
batch_size = 64

# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))

# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')

# 18. Entrenar el modelo
history = model.fit(
    X_train, y_train,
    batch_size=batch_size,
    epochs=20,
    validation_data=(X_val, y_val),
    callbacks=[early_stop],
    shuffle=False
)

# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)

# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')

# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')

train_eval_measurement_hypo_hype_1_CRNN_92 = train_eval
val_eval_measurement_hypo_hype_1_CRNN_92 = val_eval
test_eval_measurement_hypo_hype_1_CRNN_92 = test_eval

train_rmse_measurement_hypo_hype_1_CRNN_92 = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN_92 = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN_92 = test_rmse

# 22. Graficar los resultados obtenidos en el entrenamiento y validación 
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20
1577/1577 [==============================] - 3s 2ms/step - loss: 138.9516 - val_loss: 124.8335
Epoch 2/20
1577/1577 [==============================] - 2s 1ms/step - loss: 100.2275 - val_loss: 87.0507
Epoch 3/20
1577/1577 [==============================] - 2s 1ms/step - loss: 77.1367 - val_loss: 70.0232
Epoch 4/20
1577/1577 [==============================] - 2s 1ms/step - loss: 67.3278 - val_loss: 59.3543
Epoch 5/20
1577/1577 [==============================] - 2s 1ms/step - loss: 70.1445 - val_loss: 66.2480
Epoch 6/20
1577/1577 [==============================] - 2s 1ms/step - loss: 96.7198 - val_loss: 90.8945
Epoch 7/20
1577/1577 [==============================] - 2s 1ms/step - loss: 74.3784 - val_loss: 67.8310
3154/3154 [==============================] - 2s 530us/step
394/394 [==============================] - 0s 542us/step
394/394 [==============================] - 0s 577us/step
Evaluación Entrenamiento MAE: 63.52792739868164
Evaluación Validación MAE: 67.8309555053711
Evaluación Prueba MAE: 73.63329315185547
Entrenamiento RMSE: 70.96502685546875
Validación RMSE: 69.43001556396484
Prueba RMSE: 74.96488189697266

Resultado obtenido con 3 arquitecturas distintas de la CRNN¶

Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.

Las características de entrada utilizadas son:

  • 'Measurement' + Hypoglycemia + Hyperglycemia

El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.

Las arquitecturas CRNN utilizadas son:

  • Arquitectura 1 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 2 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 64. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
    • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.
  • Arquitectura 3 (hiperparámetros):
    • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
    • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
    • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
    • units (Conv1D): 32. Número de neuronas en la capa CRNN.
    • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
    • activation: 'relu'. Función de activación utilizada en la capa CRNN.
    • kernel_regularizer: L1L2 (0.001 y 0.001).
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
    • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
    • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
    • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
    • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
    • optimizer: 'SGD'. Optimizador utilizado para
    • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
    • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
    • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
    • epochs: 20. Número máximo de épocas para entrenar el modelo.

Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.

Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.

Tabla Arquitectura 1, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [308]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_1_CRNN vacía
tabla_1_CRNN_92 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_1_CRNN_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_1_CRNN_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_1_CRNN_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN_92
tabla_1_CRNN_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN_92
tabla_1_CRNN_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN_92

tabla_1_CRNN_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN_92
tabla_1_CRNN_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN_92
tabla_1_CRNN_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN_92

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(2) {
        text-align: center;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 1, CRNN
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 4.471575 Measurement + Hypo_Hyper Entrenamiento MAE 1.679124 Measurement + Hypo_Hyper Entrenamiento MAE 12.527313 Measurement + Hypo_Hyper
Validación MAE 1.541885 Measurement + Hypo_Hyper Validación MAE 0.603792 Measurement + Hypo_Hyper Validación MAE 11.934123 Measurement + Hypo_Hyper
Test MAE 2.209795 Measurement + Hypo_Hyper Test MAE 0.646110 Measurement + Hypo_Hyper Test MAE 14.363472 Measurement + Hypo_Hyper
Entrenamiento RMSE 8.106848 Measurement + Hypo_Hyper Entrenamiento RMSE 5.643665 Measurement + Hypo_Hyper Entrenamiento RMSE 15.320870 Measurement + Hypo_Hyper
Validación RMSE 1.964390 Measurement + Hypo_Hyper Validación RMSE 0.774303 Measurement + Hypo_Hyper Validación RMSE 13.936296 Measurement + Hypo_Hyper
Test RMSE 3.068929 Measurement + Hypo_Hyper Test RMSE 1.264750 Measurement + Hypo_Hyper Test RMSE 15.432524 Measurement + Hypo_Hyper
In [309]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_1_CRNN_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_1_CRNN_92 = pd.concat([tabla_1_CRNN_92, pd.DataFrame([row])])

Tabla Arquitectura 2, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo CRNN.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 64. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • units (LSTM): 32. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • Flatten: Una capa en la red neuronal que transforma una entrada multidimensional en una única dimensión.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [310]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_2_CRNN vacía
tabla_2_CRNN_92 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_2_CRNN_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_2_CRNN_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_2_CRNN_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN_92
tabla_2_CRNN_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN_92
tabla_2_CRNN_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN_92

tabla_2_CRNN_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN_92
tabla_2_CRNN_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN_92
tabla_2_CRNN_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN_92

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 69.351593
Validación MAE 74.031960
Test MAE 79.834328
Entrenamiento RMSE 76.444603
Validación RMSE 75.499474
Test RMSE 81.063759
In [311]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_2_CRNN_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_2_CRNN_92 = pd.concat([tabla_2_CRNN_92, pd.DataFrame([row])])

Tabla Arquitectura 3, CRNN¶

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units (Conv1D): 32. Número de neuronas en la capa CRNN.
  • kernel_size: 3. Filtro de convolución de tamaño 3x3 a una secuencia de entrada para extraer características locales y construir representaciones de nivel superior a medida que se procesa la información a través de la red.
  • activation: 'relu'. Función de activación utilizada en la capa CRNN.
  • kernel_regularizer: L1L2 (0.001 y 0.001).
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • MaxPooling1D: Una capa de agrupación máxima unidimensional en una red neuronal convolucional.
  • pool_size: 2. Utilizará una ventana de tamaño 2 para realizar el proceso de agrupación.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • units (LSTM): 64. Número de neuronas en la capa LSTM. Cada unidad procesa la secuencia de entrada y captura información relevante a lo largo de ella.
  • BatchNormalization: Técnica de normalización que acelera el aprendizaje y proporciona cierta regularización, generalmente mejorando el rendimiento del modelo.
  • Dropout: 0.5. Método de regularización que aleatoriamente "desactiva" algunas neuronas en una capa durante cada iteración de entrenamiento, con un ratio de 0.5, para prevenir el sobreajuste.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'SGD'. Optimizador utilizado para
  • learning_rate: 0.001. Determina el tamaño de los pasos que se toman al ajustar los pesos del modelo durante el proceso de entrenamiento.
  • momentum: 0.9. Parámetro del optimizador SGD que acelera el aprendizaje en direcciones relevantes y amortigua las oscilaciones, ayudando a encontrar el mínimo global en funciones de pérdida que no son suaves.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.
In [312]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_92 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_CRNN_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_CRNN_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_CRNN_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_92

tabla_3_CRNN_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_92

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 23.519377
Validación MAE 12.742785
Test MAE 10.551319
Entrenamiento RMSE 26.978844
Validación RMSE 15.696504
Test RMSE 14.015484
In [313]:
import pandas as pd
from IPython.display import display, HTML

# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_92 = pd.DataFrame(columns=[
    'Evaluación+Métrica',
    'Measurement + Hypo_Hyper',
])

# Definir los valores en cada posición
tabla_3_CRNN_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'

tabla_3_CRNN_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'

tabla_3_CRNN_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_92

tabla_3_CRNN_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_92

# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
    .dataframe td:nth-child(1) {
        width: 165px;
    }
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

TABLA ARQUITECTURA 3, CRNN
Evaluación+Métrica Measurement + Hypo_Hyper
Entrenamiento MAE 23.519377
Validación MAE 12.742785
Test MAE 10.551319
Entrenamiento RMSE 26.978844
Validación RMSE 15.696504
Test RMSE 14.015484
In [314]:
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']

for col in cols_to_convert:
    tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')

# Crear una nueva tabla vacía
tabla_3_CRNN_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])

# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
    min_val = tabla_copia.loc[i, 'Measurement':].min()
    min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
    row = {
        'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
        'Valor mínimo': min_val,
        'Columna correspondiente': min_col
    }
    tabla_3_CRNN_92 = pd.concat([tabla_3_CRNN_92, pd.DataFrame([row])])

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN¶

In [315]:
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN_92, tabla_2_CRNN_92, tabla_3_CRNN_92], axis=1)

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
    {'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))

COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN
Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente Evaluación+Métrica Valor mínimo Columna correspondiente
Entrenamiento MAE 63.527927 Measurement + Hypo_Hyper Entrenamiento MAE 69.351593 Measurement + Hypo_Hyper Entrenamiento MAE 23.519377 Measurement + Hypo_Hyper
Validación MAE 67.830956 Measurement + Hypo_Hyper Validación MAE 74.031960 Measurement + Hypo_Hyper Validación MAE 12.742785 Measurement + Hypo_Hyper
Test MAE 73.633293 Measurement + Hypo_Hyper Test MAE 79.834328 Measurement + Hypo_Hyper Test MAE 10.551319 Measurement + Hypo_Hyper
Entrenamiento RMSE 70.965027 Measurement + Hypo_Hyper Entrenamiento RMSE 76.444603 Measurement + Hypo_Hyper Entrenamiento RMSE 26.978844 Measurement + Hypo_Hyper
Validación RMSE 69.430016 Measurement + Hypo_Hyper Validación RMSE 75.499474 Measurement + Hypo_Hyper Validación RMSE 15.696504 Measurement + Hypo_Hyper
Test RMSE 74.964882 Measurement + Hypo_Hyper Test RMSE 81.063759 Measurement + Hypo_Hyper Test RMSE 14.015484 Measurement + Hypo_Hyper

RESULTADO DE LA COMPARATIVA A MEJOR ARQUITECTURA 1,2,3, CRNN¶

In [316]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Para cada fila
for row in rows:
    # Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
    min_val_1 = tabla_1_CRNN_92.loc[tabla_1_CRNN_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_1_CRNN_92.loc[tabla_1_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_1 = tabla_1_CRNN_92.loc[tabla_1_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_1 = None
    
    min_val_2 = tabla_2_CRNN_92.loc[tabla_2_CRNN_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_2_CRNN_92.loc[tabla_2_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_2 = tabla_2_CRNN_92.loc[tabla_2_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_2 = None
    
    min_val_3 = tabla_3_CRNN_92.loc[tabla_3_CRNN_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
    if not tabla_3_CRNN_92.loc[tabla_3_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
        min_col_3 = tabla_3_CRNN_92.loc[tabla_3_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
    else:
        min_col_3 = None
    
    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val = min(min_val_1, min_val_2, min_val_3)
    if min_val == min_val_1:
        min_col = min_col_1
        architecture = 1
    elif min_val == min_val_2:
        min_col = min_col_2
        architecture = 2
    else:
        min_col = min_col_3
        architecture = 3
    
    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')
    
    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [architecture]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))

# Aplicar el estilo personalizado al DataFrame
styled_table = result.style.applymap(bold_rows, subset=['Evaluación+Métrica']).set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
    {'selector': 'td:nth-child(4)', 'props': [('text-align', 'center')]},
    {'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])

# Mostrar el DataFrame resultante con estilo y sin índices
CRNN_92 = styled_table.hide(axis='index').to_html()

# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_92}</div>'))

RESULTADO A MEJOR ARQUITECTURA, CRNN

ARQUITECTURA 3
(Measurement + Hypo_Hyper)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 23.519377 Measurement + Hypo_Hyper 3
Validación MAE 12.742785 Measurement + Hypo_Hyper 3
Test MAE 10.551319 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 26.978844 Measurement + Hypo_Hyper 3
Validación RMSE 15.696504 Measurement + Hypo_Hyper 3
Test RMSE 14.015484 Measurement + Hypo_Hyper 3

Conclusión CRNN¶

La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.

Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.

Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.

La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.

En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.

Las fases de evaluación MAE y métrica RMSE utilizadas son:

  • Evaluación de Entrenamiento MAE y Entrenamiento RMSE: Durante el entrenamiento de un modelo de aprendizaje automático, se busca aprender de los datos de entrada y ajustar los parámetros del modelo para minimizar el error de predicción. La Evaluación Entrenamiento MAE y las métricas Raíz del Error Cuadrático Medio (RMSE) de entrenamiento son medidas que nos indican qué tan bien está aprendiendo el modelo de los datos de entrenamiento.
    • Entrenamiento MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de MAE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
    • Entrenamiento RMSE: es una métrica que calcula la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de entrenamiento. Un valor bajo de RMSE indica que las predicciones del modelo están muy cerca de los valores reales, lo que significa que el modelo está aprendiendo adecuadamente de los datos de entrenamiento.
  • Evaluación de Validación MAE y Validación RMSE: Durante la fase de validación, el modelo se prueba con un conjunto de datos diferente al de entrenamiento. Esto nos ayuda a ajustar los hiperparámetros del modelo y evitar el sobreajuste.
    • Validación MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Esta midiendo qué tan bien el modelo se ajusta a los datos de validación. Al igual que en la fase de entrenamiento, un valor bajo indica un buen ajuste del modelo a los datos de validación. El MAE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
    • Validación RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de validación. Un RMSE bajo indica que el modelo puede generalizar bien a nuevos datos, lo cual es importante para un buen modelo de aprendizaje automático.
  • Evaluación de Test MAE y Test RMSE: Esta es la etapa final en la que se prueba el modelo con un conjunto de datos de prueba que no ha sido visto durante las fases de entrenamiento y validación.
    • Test MAE: es una métrica que calcula el promedio del valor absoluto de los errores entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Esta midiendo qué tan bien el modelo se ajusta a los datos de prueba. El Test MAE bajo indica un mejor rendimiento del modelo en datos que no ha visto previamente.
    • Test RMSE: es la raíz cuadrada del promedio de los errores al cuadrado entre las predicciones del modelo y los valores reales en el conjunto de datos de prueba. Un Test RMSE bajo indica que el modelo ha aprendido correctamente y puede generalizar bien a nuevos datos, lo cual es crítico para un buen modelo de aprendizaje automático.

Análisis de los resultados:

En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.

Las razones de los resultados distintos son varias y son las siguientes:

  • Inicialización aleatoria de los parámetros: Las redes neuronales utilizan inicialización aleatoria de los pesos y sesgos iniciales. Debido a esta aleatoriedad, el modelo comienza en diferentes estados y puede converger a óptimos locales diferentes en cada ejecución.
  • Muestreo aleatorio de datos: El orden en el que se presentan los datos de entrenamiento al algoritmo puede afectar el resultado final.
  • Regularización y técnicas de regularización estocástica: El uso de regularización como L1 o L2 para controlar la complejidad del modelo y evitar sobreajuste. Esta regularización introduce términos adicionales en la función de perdida (loss), pudiendo afectar el resultado final.
  • Hiperparámetros: La configuración previa al entrenamiento del modelo lo cual no se aprende del conjunto de datos, como la tasa de aprendizaje (learning_rate), tamaño del lote, número de capas ocultas, etc. Estos hiperparámetros pueden influir en el resultado final.
  • Sensibilidad a condiciones iniciales y ruido: Los optimizadores pueden ser sensibles a las condiciones finales y al ruido de los datos. Las variaciones que puedan haber en los datos de entrenamiento o en los valores iniciales pueden influir en el resutlado final.

En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.

En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.

A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 3 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.

En un análisis global arquitectura:

  • Mejor arquitectura 3:
    • Entrenamiento MAE: Obtiene mejores resultados, alto ajuste a los datos de entrenamiento dado al valor muy bajo obtenido. Resulta con alta capacidad de realizar predicciones precisas sobre los datos.
    • Entrenamiento RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje en el entrenamiento.
    • Validación MAE: Obtiene mejores resultados, alto ajuste a los datos de validación dado al valor muy bajo obtenido.
    • Validación RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales del conjunto de datos de validación. Resulta con alta generalización a nuevos datos.
    • Test MAE: Obtiene mejores resultados, alto ajuste a los datos de prueba dado al valor muy bajo obtenido. Resulta con alto rendimiento para datos todavía no vistos.
    • Test RMSE: Obtiene mejores resultados, alto promedio en los errores al cuadrado entre las predicciones del modelo y los valores reales. Resulta con alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante.

En un análisis global características de entrada:

  • Measurement + Hypo_Hyper: En general tiene mejor resultado en Entrenamiento MAE, Validación MAE, Test MAE, Entrenamiento RMSE, Validación RMSE y Test RMSE, estas evaluaciones determinan que es capaz de realizar un alto entrenamiento, alto aprendizaje y alta generalización a nuevos datos, lo cual es muy importante para la predicción de los niveles de glucosa y para predecir los próximos 15 y 30 minutos.

La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con LSTM con las características de entrada Measurement + Hypo_Hyper.

Comparativa de resultados LSTM y CRNN¶

In [317]:
from IPython.display import display, HTML

html_tables_92 = f"""
<style>
    .table-wrapper {{
        display: inline-block;
        font-size: 14px;
        vertical-align: top;
        margin-right: 20px;
    }}
    .table-wrapper table {{
        font-size: 14px;
    }}
    .table-wrapper th, .table-wrapper td {{
        width: 100px;
    }}
    .table-wrapper h3 {{
        text-align: center;
        font-size: 18px;
        color: blue;
    }}
    h2 {{
        text-align: center;
    }}
</style>
<h2>Paciente 92</h2>
<div class="table-wrapper">
    <h3>LSTM</h3>
    {LSTM_92}
</div>
<div class="table-wrapper">
    <h3>CRNN</h3>
    {CRNN_92}
</div>
"""

display(HTML(html_tables_92))

Paciente 92

LSTM

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 1.679124 Measurement + Hypo_Hyper 2
Validación MAE 0.603792 Measurement + Hypo_Hyper 2
Test MAE 0.646110 Measurement + Hypo_Hyper 2
Entrenamiento RMSE 5.643665 Measurement + Hypo_Hyper 2
Validación RMSE 0.774303 Measurement + Hypo_Hyper 2
Test RMSE 1.264750 Measurement + Hypo_Hyper 2

CRNN

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura
Entrenamiento MAE 23.519377 Measurement + Hypo_Hyper 3
Validación MAE 12.742785 Measurement + Hypo_Hyper 3
Test MAE 10.551319 Measurement + Hypo_Hyper 3
Entrenamiento RMSE 26.978844 Measurement + Hypo_Hyper 3
Validación RMSE 15.696504 Measurement + Hypo_Hyper 3
Test RMSE 14.015484 Measurement + Hypo_Hyper 3

Mejor Modelo (Paciente 92)¶

In [318]:
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])

# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']

# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN_92': 1, 'tabla_2_CRNN_92': 2, 'tabla_3_CRNN_92': 3}
tables_arch2 = {'tabla_1_LSTM_92': 1, 'tabla_2_LSTM_92': 2, 'tabla_3_LSTM_92': 3}

# Para cada fila
for row in rows:
    min_vals = []
    
    # Arquitectura 1
    for table_name, arch in tables_arch1.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'CRNN', arch))

    # Arquitectura 2
    for table_name, arch in tables_arch2.items():
        min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
        if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
            min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
        else:
            min_col = None
        min_vals.append((min_val, min_col, 'LSTM', arch))

    # Encontrar el valor mínimo global y la columna y arquitectura correspondientes
    min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])

    # Quitar las etiquetas <b> y </b> de la variable row
    row_without_tags = row.replace('<b>', '').replace('</b>', '')

    # Agregar una fila al DataFrame result usando pandas.concat
    result = pd.concat([result, pd.DataFrame({
        'Evaluación+Métrica': [row_without_tags],
        'Valor mínimo': [min_val],
        'Columna correspondiente': [min_col],
        'Arquitectura': [min_arch],
        'Arquitectura (Hiperparámetros)': [min_num]
    })], ignore_index=True)

# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
    if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
        return 'font-weight: bold'
    return ''

# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 92)</span></center>"
display(HTML(html_text))

# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()

# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))

# Nombrando
styled_result5 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result5 = styled_result5.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos

# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_92.style.set_table_styles([{
    'selector': 'th, td',
    'props': [('text-align', 'center')]
}])

html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"

# Muestra el HTML
display(HTML(html))

# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
    display: table-cell;
    text-align: center;
    vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))


# Mostrar la figura
display(fig_hypo_hype_2_LSTM_92)

# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_92.style.set_properties(**{'text-align': 'center'}).hide(axis='index')

# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"

# Mostrar la tabla centrada
display(HTML(table_html))

MEJOR ARQUITECTURA (Paciente 92)
Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 1.679124 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.603792 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.646110 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 5.643665 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 0.774303 Measurement + Hypo_Hyper LSTM 2
Test RMSE 1.264750 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-01-10 16:15:00 nan 186.222000
2022-01-10 16:30:00 nan 185.486877

Precisión del modelo con Clarke Error Grid

Zona Conteo Proporción
A 12607 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Conclusión Paciente 92¶

Los resultados nos aclara que la mejor opción para el paciente 92 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.

Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):

  • seq_length: 8. Longitud de las secuencias de entrada al modelo LSTM.
  • patience: 3. Número de épocas sin mejora en la pérdida de validación antes de detener el entrenamiento.
  • batch_size: 64. Tamaño del lote para el entrenamiento del modelo.
  • units: 64. Número de neuronas en la capa LSTM.
  • activation: 'relu'. Función de activación utilizada en la capa LSTM.
  • Dense: 1. Capa de neuronas completamente conectadas, en este caso con 1 neurona de salida.
  • optimizer: 'adam'. Optimizador utilizado para actualizar los pesos del modelo durante el entrenamiento.
  • loss: ‘mean_absolute_error’. Función de pérdida utilizada para calcular el error durante el entrenamiento.
  • epochs: 20. Número máximo de épocas para entrenar el modelo.

La conclusión final para el paciente 92 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hypo_Hyper para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.

4. Evaluación de los 5 pacientes con mejores arquitecturas¶

In [441]:
# Definir los títulos
titles = ["MEJOR ARQUITECTURA (Paciente 22)", "MEJOR ARQUITECTURA (Paciente 24)", "MEJOR ARQUITECTURA (Paciente 11)", 
          "MEJOR ARQUITECTURA (Paciente 83)", "MEJOR ARQUITECTURA (Paciente 92)"]

# Lista de DataFrames para las predicciones futuras de los pacientes
future_predictions = [future_predictions_hypo_hype_2_LSTM_22, future_predictions_hypo_hype_2_LSTM_24, future_predictions_hypo_hype_2_LSTM_11, 
                      future_predictions_hypo_hype_2_LSTM_83, future_predictions_hypo_hype_2_LSTM_92]

# Lista de los resultados
results = [styled_result1, styled_result2, styled_result3, styled_result4, styled_result5]

# Lista de figuras y tablas zone_dfs para cada paciente
figures = [fig_hypo_hype_2_LSTM_22, fig_hypo_hype_2_LSTM_24, fig_hypo_hype_2_LSTM_11, fig_hypo_hype_2_LSTM_83, fig_hypo_hype_2_LSTM_92]
zone_dfs = [zone_df_hypo_hype_2_LSTM_22, zone_df_hypo_hype_2_LSTM_24, zone_df_hypo_hype_2_LSTM_11, zone_df_hypo_hype_2_LSTM_83, zone_df_hypo_hype_2_LSTM_92]

# Lista para guardar las tablas HTML
tables = []

# Bucle para crear y formatear las tablas
for i in range(5):
    # Aplicar el estilo personalizado al DataFrame para centrar las columnas específicas
    styled_df = future_predictions[i].style.set_table_styles([
        {'selector': '.col0, .col1', 'props': [('text-align', 'center')]}
    ])
    html_table = results[i].hide(axis='index').to_html().replace("<table", "<table style='margin: 0 auto;'") + \
    f"""
    <div style='text-align: center;'>
    <h3>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h3>
    """ + styled_df.to_html().replace("<table", "<table style='margin: 0 auto;'") + "</div><br>"
    # Generar un div para cada tabla con la clase 'table-container' y añadir los títulos
    html_table = f"<div class='table-container'><h2>{titles[i]}</h2>{html_table}</div>"
    # Añadir la tabla a la lista de tablas
    tables.append(html_table)

# Bucle para mostrar las tablas, títulos y gráficas de cada paciente
for i in range(5):
    # Mostrar la tabla
    display(HTML(tables[i]))
    
    # Mostrar la gráfica
    display(figures[i])
    
    # Aplicar el estilo personalizado al DataFrame para centrar las columnas
    styled_df = zone_dfs[i].style.set_table_styles([
        {'selector': '.col0, .col1', 'props': [('text-align', 'center')]}
    ])
    html_table_zone_dfs = styled_df.hide(axis='index').to_html().replace("<table", "<table style='margin: 0 auto;'") + "<br>"
    
    # Mostrar la tabla zone_dfs
    display(HTML(html_table_zone_dfs))

MEJOR ARQUITECTURA (Paciente 22)

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 1.083968 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.271668 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.231488 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 4.527636 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 1.200419 Measurement + Hypo_Hyper LSTM 2
Test RMSE 1.222260 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-03-16 00:01:00 nan 156.141495
2022-03-16 00:16:00 nan 157.410568

Zona Conteo Proporción
A 11758 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

MEJOR ARQUITECTURA (Paciente 24)

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 1.848579 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.794391 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.856952 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 5.513992 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 0.927484 Measurement + Hypo_Hyper LSTM 2
Test RMSE 0.975038 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-03-12 08:45:00 nan 156.134811
2022-03-12 09:00:00 nan 155.769943

Zona Conteo Proporción
A 12836 100.00%
B 0 0.00%
C 0 0.00%
D 0 0.00%
E 0 0.00%

MEJOR ARQUITECTURA (Paciente 11)

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 1.460966 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.849688 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.868796 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 4.643505 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 1.428916 Measurement + Hypo_Hyper LSTM 2
Test RMSE 1.406802 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-02-25 19:30:00 nan 179.642593
2022-02-25 19:45:00 nan 173.928604

Zona Conteo Proporción
A 12989 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

MEJOR ARQUITECTURA (Paciente 83)

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 0.862907 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.610178 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.465580 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 3.425832 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 1.738084 Measurement + Hypo_Hyper LSTM 2
Test RMSE 1.239297 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-03-14 11:00:00 nan 166.793915
2022-03-14 11:15:00 nan 165.903671

Zona Conteo Proporción
A 14216 100.00%
B 0 0.00%
C 0 0.00%
D 0 0.00%
E 0 0.00%

MEJOR ARQUITECTURA (Paciente 92)

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 1.679124 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.603792 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.646110 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 5.643665 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 0.774303 Measurement + Hypo_Hyper LSTM 2
Test RMSE 1.264750 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-01-10 16:15:00 nan 186.222000
2022-01-10 16:30:00 nan 185.486877

Zona Conteo Proporción
A 12607 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Arquitectura con mayor rendimiento¶

In [442]:
# Dataframes originales
df1 = styled_result1.data.copy()
df2 = styled_result2.data.copy()
df3 = styled_result3.data.copy()
df4 = styled_result4.data.copy()
df5 = styled_result5.data.copy()

# Obtener los valores mínimos de cada tabla
min_val1 = df1['Valor mínimo'].min()
min_val2 = df2['Valor mínimo'].min()
min_val3 = df3['Valor mínimo'].min()
min_val4 = df4['Valor mínimo'].min()
min_val5 = df5['Valor mínimo'].min()

# Identificar qué tabla tiene el valor mínimo más bajo
min_table_val = min(min_val1, min_val2, min_val3, min_val4, min_val5)

if min_table_val == min_val1:
    min_table_name = "MEJOR ARQUITECTURA (Paciente 22)"
    min_table = df1
    future_prediction = future_predictions_hypo_hype_2_LSTM_22
    result = styled_result1
    figure = fig_hypo_hype_2_LSTM_22
    zone_df = zone_df_hypo_hype_2_LSTM_22
elif min_table_val == min_val2:
    min_table_name = "MEJOR ARQUITECTURA (Paciente 24)"
    min_table = df2
    future_prediction = future_predictions_hypo_hype_2_LSTM_24
    result = styled_result2
    figure = fig_hypo_hype_2_LSTM_24
    zone_df = zone_df_hypo_hype_2_LSTM_24
elif min_table_val == min_val3:
    min_table_name = "MEJOR ARQUITECTURA (Paciente 11)"
    min_table = df3
    future_prediction = future_predictions_hypo_hype_2_LSTM_11
    result = styled_result3
    figure = fig_hypo_hype_2_LSTM_11
    zone_df = zone_df_hypo_hype_2_LSTM_11
elif min_table_val == min_val4:
    min_table_name = "MEJOR ARQUITECTURA (Paciente 83)"
    min_table = df4
    future_prediction = future_predictions_hypo_hype_2_LSTM_83
    result = styled_result4
    figure = fig_hypo_hype_2_LSTM_83
    zone_df = zone_df_hypo_hype_2_LSTM_83
else:
    min_table_name = "MEJOR ARQUITECTURA (Paciente 92)"
    min_table = df5
    future_prediction = future_predictions_hypo_hype_2_LSTM_92
    result = styled_result5
    figure = fig_hypo_hype_2_LSTM_92
    zone_df = zone_df_hypo_hype_2_LSTM_92

print(f"La tabla con el valor mínimo más bajo es: {min_table_name}")

# Definir el título para la tabla con el valor mínimo más bajo como Mejor Arquitectura (Paciente X)
min_title = f"{min_table_name}"

# Aplicar el estilo personalizado al DataFrame para centrar las columnas específicas
styled_df = future_prediction.style.set_table_styles([
        {'selector': '.col0, .col1', 'props': [('text-align', 'center')]}
])
html_table = result.hide(axis='index').to_html().replace("<table", "<table style='margin: 0 auto;'") + \
f"""
<div style='text-align: center;'>
<h3>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h3>
""" + styled_df.to_html().replace("<table", "<table style='margin: 0 auto;'") + "</div><br>"
# Generar un div para cada tabla con la clase 'table-container' y añadir los títulos y el título adicional centrado y subrayado.
html_table = f"<div class='table-container'><h1 style='text-align: center; text-decoration: underline;'>ARQUITECTURA CON MAYOR RENDIMIENTO</h1><h2>{min_title}</h2>{html_table}</div>"

# Mostrar la tabla y la gráfica del paciente con el valor mínimo más bajo

# Mostrar la tabla de resultados y predicciones futuras del paciente con el valor mínimo más bajo.
display(HTML(html_table))

# Mostrar la gráfica del paciente con el valor mínimo más bajo.
display(figure)

# Aplicar el estilo personalizado al DataFrame para centrar las columnas.
styled_df_zone_dfs_min_patient=zone_df.style.set_table_styles([
        {'selector': '.col0, .col1', 'props': [('text-align', 'center')]}
])
html_table_zone_dfs_min_patient=styled_df_zone_dfs_min_patient.hide(axis='index').to_html().replace("<table", "<table style='margin: 0 auto;'") + "<br>"

# Mostrar la tabla zone_dfs del paciente con el valor mínimo más bajo.
display(HTML(html_table_zone_dfs_min_patient))
La tabla con el valor mínimo más bajo es: MEJOR ARQUITECTURA (Paciente 22)

ARQUITECTURA CON MAYOR RENDIMIENTO

MEJOR ARQUITECTURA (Paciente 22)

Evaluación+Métrica Valor mínimo Columna correspondiente Arquitectura Arquitectura (Hiperparámetros)
Entrenamiento MAE 1.083968 Measurement + Hypo_Hyper LSTM 2
Validación MAE 0.271668 Measurement + Hypo_Hyper LSTM 2
Test MAE 0.231488 Measurement + Hypo_Hyper LSTM 2
Entrenamiento RMSE 4.527636 Measurement + Hypo_Hyper LSTM 2
Validación RMSE 1.200419 Measurement + Hypo_Hyper LSTM 2
Test RMSE 1.222260 Measurement + Hypo_Hyper LSTM 2

Predicción del nivel de glucosa en los próximos 15 y 30 minutos

  Datos reales Datos predichos
date    
2022-03-16 00:01:00 nan 156.141495
2022-03-16 00:16:00 nan 157.410568

Zona Conteo Proporción
A 11758 99.99%
B 1 0.01%
C 0 0.00%
D 0 0.00%
E 0 0.00%

Evaluación y Conclusión¶

El análisis llevado a cabo en este estudio refleja una superioridad de la arquitectura LSTM con hiperparámetros 2 y las características de entrada 'Measurement + Hypo_Hyper' en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos. Esta evaluación ha surgido de la comparación exhaustiva entre diferentes arquitecturas y algoritmos, incluyendo LSTM y CRNN, y una variedad de características de entrada. Esta superioridad se mantiene constante independientemente de las particularidades de los datos de entrada. La evaluación se ha aplicado a cinco pacientes seleccionados (pacientes 24, 11, 22, 83 y 92) de un conjunto de 110, generando pronósticos altamente precisos de los niveles de glucosa a corto plazo. En cada caso, los valores de predicción a 15 y 30 minutos han demostrado un mínimo Error de raíz cuadrada media (RMSE), lo que refleja la capacidad del modelo LSTM para proporcionar predicciones precisas para datos no vistos previamente.

Evaluación de los resultados finales de los 5 pacientes:

  • La arquitectura del paciente 22 con las características de entrada Measurement + Hypo_Hyper ofrecen una predicción para los próximos 15 minutos de nivel de glucosa de 156.141495 y 30 minutos de nivel de glucosa de 157.410568. Estos valores son muy precisos dado que el resultado del Test RMSE arroja un valor mínimo de 1.222260. Esto nos asegura tener un control muy alto y con mucha precisión a datos no conocidos como son los próximos 15 y 30 minutos.
  • La arquitectura del paciente 24 con las características de entrada Measurement + Hypo_Hyper ofrecen una predicción para los próximos 15 minutos de nivel de glucosa de 156.134811 y 30 minutos de nivel de glucosa de 155.769943. Estos valores son muy precisos dado que el resultado del Test RMSE arroja un valor mínimo de 0.975038. Esto nos asegura tener un control muy alto y con mucha precisión a datos no conocidos como son los próximos 15 y 30 minutos.
  • La arquitectura del paciente 11 con las características de entrada Measurement + Hypo_Hyper ofrecen una predicción para los próximos 15 minutos de nivel de glucosa de 179.642593 y 30 minutos de nivel de glucosa de 173.928604. Estos valores son muy precisos dado que el resultado del Test RMSE arroja un valor mínimo de 1.406802. Esto nos asegura tener un control muy alto y con mucha precisión a datos no conocidos como son los próximos 15 y 30 minutos.
  • La arquitectura del paciente 83 con las características de entrada Measurement + Hypo_Hyper ofrecen una predicción para los próximos 15 minutos de nivel de glucosa de 166.793915 y 30 minutos de nivel de glucosa de 165.903671. Estos valores son muy precisos dado que el resultado del Test RMSE arroja un valor mínimo de 1.239297. Esto nos asegura tener un control muy alto y con mucha precisión a datos no conocidos como son los próximos 15 y 30 minutos.
  • La arquitectura del paciente 92 con las características de entrada Measurement + Hypo_Hyper ofrecen una predicción para los próximos 15 minutos de nivel de glucosa de 186.222000 y 30 minutos de nivel de glucosa de 185.486877. Estos valores son muy precisos dado que el resultado del RMSE Test arroja un valor mínimo de 1.264750. Esto nos asegura tener un control muy alto y con mucha precisión a datos no conocidos como son los próximos 15 y 30 minutos.

El hecho de que estos resultados sean consistentes y altamente precisos en todos los pacientes analizados proporciona una sólida verificación de la eficacia del enfoque propuesto. Por ende, podemos afirmar con confianza que el LSTM con la configuración de hiperparámetros 2 y las características de entrada 'Measurement + Hypo_Hyper' es un modelo efectivo para la predicción de los niveles de glucosa.

Es importante resaltar que el modelo LSTM exhibió una alta precisión tanto en datos de entrenamiento como en datos de prueba. Esta característica indica que el modelo está bien ajustado, sin signos de sobreajuste (overfitting) o subajuste (underfitting), lo que sugiere que tiene la capacidad de generalizar bien a datos no vistos. Esta es una cualidad esencial que refuerza la fiabilidad del modelo LSTM para la predicción de los niveles de glucosa en pacientes con diabetes.

En conclusión, los resultados obtenidos en esta investigación subrayan la eficacia de las Redes Neuronales Recurrentes de Memoria a Largo Plazo (LSTM) para predecir los niveles de glucosa en pacientes con diabetes. El alto rendimiento del modelo LSTM, confirmado a través de la comparación con la arquitectura CRNN y la variedad de características de entrada, sugiere que tiene un gran potencial para ser utilizado como una herramienta precisa y confiable en la gestión futura de la diabetes.